mirror of
https://github.com/space-syndicate/space-station-14.git
synced 2026-10-09 10:21:18 +03:00
Log custom votes to database. (#42650)
* Log custom votes to database. * Add navigation property from round entity
This commit is contained in:
parent
0cd25ac653
commit
9da1cff855
12 changed files with 5044 additions and 3 deletions
2235
Content.Server.Database/Migrations/Postgres/20260126003838_CustomVoteLog.Designer.cs
generated
Normal file
2235
Content.Server.Database/Migrations/Postgres/20260126003838_CustomVoteLog.Designer.cs
generated
Normal file
File diff suppressed because it is too large
Load diff
|
|
@ -0,0 +1,85 @@
|
|||
using System;
|
||||
using Microsoft.EntityFrameworkCore.Migrations;
|
||||
using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata;
|
||||
|
||||
#nullable disable
|
||||
|
||||
namespace Content.Server.Database.Migrations.Postgres
|
||||
{
|
||||
/// <inheritdoc />
|
||||
public partial class CustomVoteLog : Migration
|
||||
{
|
||||
/// <inheritdoc />
|
||||
protected override void Up(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
migrationBuilder.CreateTable(
|
||||
name: "custom_vote_log",
|
||||
columns: table => new
|
||||
{
|
||||
custom_vote_log_id = table.Column<int>(type: "integer", nullable: false)
|
||||
.Annotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn),
|
||||
round_id = table.Column<int>(type: "integer", nullable: false),
|
||||
time_created = table.Column<DateTime>(type: "timestamp with time zone", nullable: false),
|
||||
title = table.Column<string>(type: "text", nullable: false),
|
||||
initiator_id = table.Column<Guid>(type: "uuid", nullable: true),
|
||||
state = table.Column<byte>(type: "smallint", nullable: false)
|
||||
},
|
||||
constraints: table =>
|
||||
{
|
||||
table.PrimaryKey("PK_custom_vote_log", x => x.custom_vote_log_id);
|
||||
table.ForeignKey(
|
||||
name: "FK_custom_vote_log_player_initiator_id1",
|
||||
column: x => x.initiator_id,
|
||||
principalTable: "player",
|
||||
principalColumn: "user_id",
|
||||
onDelete: ReferentialAction.SetNull);
|
||||
table.ForeignKey(
|
||||
name: "FK_custom_vote_log_round_round_id",
|
||||
column: x => x.round_id,
|
||||
principalTable: "round",
|
||||
principalColumn: "round_id",
|
||||
onDelete: ReferentialAction.Cascade);
|
||||
});
|
||||
|
||||
migrationBuilder.CreateTable(
|
||||
name: "custom_vote_log_option",
|
||||
columns: table => new
|
||||
{
|
||||
vote_id = table.Column<int>(type: "integer", nullable: false),
|
||||
option_idx = table.Column<short>(type: "smallint", nullable: false),
|
||||
text = table.Column<string>(type: "text", nullable: false),
|
||||
vote_count = table.Column<int>(type: "integer", nullable: false)
|
||||
},
|
||||
constraints: table =>
|
||||
{
|
||||
table.PrimaryKey("PK_custom_vote_log_option", x => new { x.vote_id, x.option_idx });
|
||||
table.ForeignKey(
|
||||
name: "FK_custom_vote_log_option_custom_vote_log_vote_id",
|
||||
column: x => x.vote_id,
|
||||
principalTable: "custom_vote_log",
|
||||
principalColumn: "custom_vote_log_id",
|
||||
onDelete: ReferentialAction.Cascade);
|
||||
});
|
||||
|
||||
migrationBuilder.CreateIndex(
|
||||
name: "IX_custom_vote_log_initiator_id",
|
||||
table: "custom_vote_log",
|
||||
column: "initiator_id");
|
||||
|
||||
migrationBuilder.CreateIndex(
|
||||
name: "IX_custom_vote_log_round_id",
|
||||
table: "custom_vote_log",
|
||||
column: "round_id");
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override void Down(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
migrationBuilder.DropTable(
|
||||
name: "custom_vote_log_option");
|
||||
|
||||
migrationBuilder.DropTable(
|
||||
name: "custom_vote_log");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -849,6 +849,72 @@ namespace Content.Server.Database.Migrations.Postgres
|
|||
});
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Content.Server.Database.CustomVoteLog", b =>
|
||||
{
|
||||
b.Property<int>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("integer")
|
||||
.HasColumnName("custom_vote_log_id");
|
||||
|
||||
NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property<int>("Id"));
|
||||
|
||||
b.Property<Guid?>("InitiatorId")
|
||||
.HasColumnType("uuid")
|
||||
.HasColumnName("initiator_id");
|
||||
|
||||
b.Property<int>("RoundId")
|
||||
.HasColumnType("integer")
|
||||
.HasColumnName("round_id");
|
||||
|
||||
b.Property<byte>("State")
|
||||
.HasColumnType("smallint")
|
||||
.HasColumnName("state");
|
||||
|
||||
b.Property<DateTime>("TimeCreated")
|
||||
.HasColumnType("timestamp with time zone")
|
||||
.HasColumnName("time_created");
|
||||
|
||||
b.Property<string>("Title")
|
||||
.IsRequired()
|
||||
.HasColumnType("text")
|
||||
.HasColumnName("title");
|
||||
|
||||
b.HasKey("Id")
|
||||
.HasName("PK_custom_vote_log");
|
||||
|
||||
b.HasIndex("InitiatorId");
|
||||
|
||||
b.HasIndex("RoundId")
|
||||
.HasDatabaseName("IX_custom_vote_log_round_id");
|
||||
|
||||
b.ToTable("custom_vote_log", (string)null);
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Content.Server.Database.CustomVoteLogOption", b =>
|
||||
{
|
||||
b.Property<int>("VoteId")
|
||||
.HasColumnType("integer")
|
||||
.HasColumnName("vote_id");
|
||||
|
||||
b.Property<short>("OptionIdx")
|
||||
.HasColumnType("smallint")
|
||||
.HasColumnName("option_idx");
|
||||
|
||||
b.Property<string>("Text")
|
||||
.IsRequired()
|
||||
.HasColumnType("text")
|
||||
.HasColumnName("text");
|
||||
|
||||
b.Property<int>("VoteCount")
|
||||
.HasColumnType("integer")
|
||||
.HasColumnName("vote_count");
|
||||
|
||||
b.HasKey("VoteId", "OptionIdx")
|
||||
.HasName("PK_custom_vote_log_option");
|
||||
|
||||
b.ToTable("custom_vote_log_option", (string)null);
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Content.Server.Database.IPIntelCache", b =>
|
||||
{
|
||||
b.Property<int>("Id")
|
||||
|
|
@ -1831,6 +1897,39 @@ namespace Content.Server.Database.Migrations.Postgres
|
|||
b.Navigation("Server");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Content.Server.Database.CustomVoteLog", b =>
|
||||
{
|
||||
b.HasOne("Content.Server.Database.Player", "Initiator")
|
||||
.WithMany()
|
||||
.HasForeignKey("InitiatorId")
|
||||
.HasPrincipalKey("UserId")
|
||||
.OnDelete(DeleteBehavior.SetNull)
|
||||
.HasConstraintName("FK_custom_vote_log_player_initiator_id1");
|
||||
|
||||
b.HasOne("Content.Server.Database.Round", "Round")
|
||||
.WithMany("CustomVoteLogs")
|
||||
.HasForeignKey("RoundId")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired()
|
||||
.HasConstraintName("FK_custom_vote_log_round_round_id");
|
||||
|
||||
b.Navigation("Initiator");
|
||||
|
||||
b.Navigation("Round");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Content.Server.Database.CustomVoteLogOption", b =>
|
||||
{
|
||||
b.HasOne("Content.Server.Database.CustomVoteLog", "Vote")
|
||||
.WithMany("Options")
|
||||
.HasForeignKey("VoteId")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired()
|
||||
.HasConstraintName("FK_custom_vote_log_option_custom_vote_log_vote_id");
|
||||
|
||||
b.Navigation("Vote");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Content.Server.Database.Job", b =>
|
||||
{
|
||||
b.HasOne("Content.Server.Database.Profile", "Profile")
|
||||
|
|
@ -2048,6 +2147,11 @@ namespace Content.Server.Database.Migrations.Postgres
|
|||
b.Navigation("BanHits");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Content.Server.Database.CustomVoteLog", b =>
|
||||
{
|
||||
b.Navigation("Options");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Content.Server.Database.Player", b =>
|
||||
{
|
||||
b.Navigation("AdminLogs");
|
||||
|
|
@ -2112,6 +2216,8 @@ namespace Content.Server.Database.Migrations.Postgres
|
|||
modelBuilder.Entity("Content.Server.Database.Round", b =>
|
||||
{
|
||||
b.Navigation("AdminLogs");
|
||||
|
||||
b.Navigation("CustomVoteLogs");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Content.Server.Database.Server", b =>
|
||||
|
|
|
|||
2152
Content.Server.Database/Migrations/Sqlite/20260126003831_CustomVoteLog.Designer.cs
generated
Normal file
2152
Content.Server.Database/Migrations/Sqlite/20260126003831_CustomVoteLog.Designer.cs
generated
Normal file
File diff suppressed because it is too large
Load diff
|
|
@ -0,0 +1,84 @@
|
|||
using System;
|
||||
using Microsoft.EntityFrameworkCore.Migrations;
|
||||
|
||||
#nullable disable
|
||||
|
||||
namespace Content.Server.Database.Migrations.Sqlite
|
||||
{
|
||||
/// <inheritdoc />
|
||||
public partial class CustomVoteLog : Migration
|
||||
{
|
||||
/// <inheritdoc />
|
||||
protected override void Up(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
migrationBuilder.CreateTable(
|
||||
name: "custom_vote_log",
|
||||
columns: table => new
|
||||
{
|
||||
custom_vote_log_id = table.Column<int>(type: "INTEGER", nullable: false)
|
||||
.Annotation("Sqlite:Autoincrement", true),
|
||||
round_id = table.Column<int>(type: "INTEGER", nullable: false),
|
||||
time_created = table.Column<DateTime>(type: "TEXT", nullable: false),
|
||||
title = table.Column<string>(type: "TEXT", nullable: false),
|
||||
initiator_id = table.Column<Guid>(type: "TEXT", nullable: true),
|
||||
state = table.Column<byte>(type: "INTEGER", nullable: false)
|
||||
},
|
||||
constraints: table =>
|
||||
{
|
||||
table.PrimaryKey("PK_custom_vote_log", x => x.custom_vote_log_id);
|
||||
table.ForeignKey(
|
||||
name: "FK_custom_vote_log_player_initiator_id1",
|
||||
column: x => x.initiator_id,
|
||||
principalTable: "player",
|
||||
principalColumn: "user_id",
|
||||
onDelete: ReferentialAction.SetNull);
|
||||
table.ForeignKey(
|
||||
name: "FK_custom_vote_log_round_round_id",
|
||||
column: x => x.round_id,
|
||||
principalTable: "round",
|
||||
principalColumn: "round_id",
|
||||
onDelete: ReferentialAction.Cascade);
|
||||
});
|
||||
|
||||
migrationBuilder.CreateTable(
|
||||
name: "custom_vote_log_option",
|
||||
columns: table => new
|
||||
{
|
||||
vote_id = table.Column<int>(type: "INTEGER", nullable: false),
|
||||
option_idx = table.Column<short>(type: "INTEGER", nullable: false),
|
||||
text = table.Column<string>(type: "TEXT", nullable: false),
|
||||
vote_count = table.Column<int>(type: "INTEGER", nullable: false)
|
||||
},
|
||||
constraints: table =>
|
||||
{
|
||||
table.PrimaryKey("PK_custom_vote_log_option", x => new { x.vote_id, x.option_idx });
|
||||
table.ForeignKey(
|
||||
name: "FK_custom_vote_log_option_custom_vote_log_vote_id",
|
||||
column: x => x.vote_id,
|
||||
principalTable: "custom_vote_log",
|
||||
principalColumn: "custom_vote_log_id",
|
||||
onDelete: ReferentialAction.Cascade);
|
||||
});
|
||||
|
||||
migrationBuilder.CreateIndex(
|
||||
name: "IX_custom_vote_log_initiator_id",
|
||||
table: "custom_vote_log",
|
||||
column: "initiator_id");
|
||||
|
||||
migrationBuilder.CreateIndex(
|
||||
name: "IX_custom_vote_log_round_id",
|
||||
table: "custom_vote_log",
|
||||
column: "round_id");
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override void Down(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
migrationBuilder.DropTable(
|
||||
name: "custom_vote_log_option");
|
||||
|
||||
migrationBuilder.DropTable(
|
||||
name: "custom_vote_log");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -798,6 +798,70 @@ namespace Content.Server.Database.Migrations.Sqlite
|
|||
b.ToTable("connection_log", (string)null);
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Content.Server.Database.CustomVoteLog", b =>
|
||||
{
|
||||
b.Property<int>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("INTEGER")
|
||||
.HasColumnName("custom_vote_log_id");
|
||||
|
||||
b.Property<Guid?>("InitiatorId")
|
||||
.HasColumnType("TEXT")
|
||||
.HasColumnName("initiator_id");
|
||||
|
||||
b.Property<int>("RoundId")
|
||||
.HasColumnType("INTEGER")
|
||||
.HasColumnName("round_id");
|
||||
|
||||
b.Property<byte>("State")
|
||||
.HasColumnType("INTEGER")
|
||||
.HasColumnName("state");
|
||||
|
||||
b.Property<DateTime>("TimeCreated")
|
||||
.HasColumnType("TEXT")
|
||||
.HasColumnName("time_created");
|
||||
|
||||
b.Property<string>("Title")
|
||||
.IsRequired()
|
||||
.HasColumnType("TEXT")
|
||||
.HasColumnName("title");
|
||||
|
||||
b.HasKey("Id")
|
||||
.HasName("PK_custom_vote_log");
|
||||
|
||||
b.HasIndex("InitiatorId");
|
||||
|
||||
b.HasIndex("RoundId")
|
||||
.HasDatabaseName("IX_custom_vote_log_round_id");
|
||||
|
||||
b.ToTable("custom_vote_log", (string)null);
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Content.Server.Database.CustomVoteLogOption", b =>
|
||||
{
|
||||
b.Property<int>("VoteId")
|
||||
.HasColumnType("INTEGER")
|
||||
.HasColumnName("vote_id");
|
||||
|
||||
b.Property<short>("OptionIdx")
|
||||
.HasColumnType("INTEGER")
|
||||
.HasColumnName("option_idx");
|
||||
|
||||
b.Property<string>("Text")
|
||||
.IsRequired()
|
||||
.HasColumnType("TEXT")
|
||||
.HasColumnName("text");
|
||||
|
||||
b.Property<int>("VoteCount")
|
||||
.HasColumnType("INTEGER")
|
||||
.HasColumnName("vote_count");
|
||||
|
||||
b.HasKey("VoteId", "OptionIdx")
|
||||
.HasName("PK_custom_vote_log_option");
|
||||
|
||||
b.ToTable("custom_vote_log_option", (string)null);
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Content.Server.Database.IPIntelCache", b =>
|
||||
{
|
||||
b.Property<int>("Id")
|
||||
|
|
@ -1750,6 +1814,39 @@ namespace Content.Server.Database.Migrations.Sqlite
|
|||
b.Navigation("Server");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Content.Server.Database.CustomVoteLog", b =>
|
||||
{
|
||||
b.HasOne("Content.Server.Database.Player", "Initiator")
|
||||
.WithMany()
|
||||
.HasForeignKey("InitiatorId")
|
||||
.HasPrincipalKey("UserId")
|
||||
.OnDelete(DeleteBehavior.SetNull)
|
||||
.HasConstraintName("FK_custom_vote_log_player_initiator_id1");
|
||||
|
||||
b.HasOne("Content.Server.Database.Round", "Round")
|
||||
.WithMany("CustomVoteLogs")
|
||||
.HasForeignKey("RoundId")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired()
|
||||
.HasConstraintName("FK_custom_vote_log_round_round_id");
|
||||
|
||||
b.Navigation("Initiator");
|
||||
|
||||
b.Navigation("Round");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Content.Server.Database.CustomVoteLogOption", b =>
|
||||
{
|
||||
b.HasOne("Content.Server.Database.CustomVoteLog", "Vote")
|
||||
.WithMany("Options")
|
||||
.HasForeignKey("VoteId")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired()
|
||||
.HasConstraintName("FK_custom_vote_log_option_custom_vote_log_vote_id");
|
||||
|
||||
b.Navigation("Vote");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Content.Server.Database.Job", b =>
|
||||
{
|
||||
b.HasOne("Content.Server.Database.Profile", "Profile")
|
||||
|
|
@ -1967,6 +2064,11 @@ namespace Content.Server.Database.Migrations.Sqlite
|
|||
b.Navigation("BanHits");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Content.Server.Database.CustomVoteLog", b =>
|
||||
{
|
||||
b.Navigation("Options");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Content.Server.Database.Player", b =>
|
||||
{
|
||||
b.Navigation("AdminLogs");
|
||||
|
|
@ -2031,6 +2133,8 @@ namespace Content.Server.Database.Migrations.Sqlite
|
|||
modelBuilder.Entity("Content.Server.Database.Round", b =>
|
||||
{
|
||||
b.Navigation("AdminLogs");
|
||||
|
||||
b.Navigation("CustomVoteLogs");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Content.Server.Database.Server", b =>
|
||||
|
|
|
|||
129
Content.Server.Database/Model.CustomVoteLog.cs
Normal file
129
Content.Server.Database/Model.CustomVoteLog.cs
Normal file
|
|
@ -0,0 +1,129 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
|
||||
namespace Content.Server.Database;
|
||||
|
||||
//
|
||||
// Contains model definitions primarily related to custom vote logging.
|
||||
//
|
||||
|
||||
internal static class ModelCustomVoteLog
|
||||
{
|
||||
public static void OnModelCreating(ModelBuilder modelBuilder)
|
||||
{
|
||||
modelBuilder.Entity<CustomVoteLog>()
|
||||
.HasOne(cvl => cvl.Initiator)
|
||||
.WithMany()
|
||||
.HasForeignKey(cvl => cvl.InitiatorId)
|
||||
.HasPrincipalKey(p => p.UserId)
|
||||
.OnDelete(DeleteBehavior.SetNull)
|
||||
.IsRequired(false);
|
||||
|
||||
modelBuilder.Entity<CustomVoteLogOption>()
|
||||
.HasKey(cvl => new { cvl.VoteId, cvl.OptionIdx });
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// A single admin-initiated custom vote logged in the database.
|
||||
/// </summary>
|
||||
public sealed class CustomVoteLog
|
||||
{
|
||||
public int Id { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// The round ID the vote was made in.
|
||||
/// </summary>
|
||||
public int RoundId { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// The time the vote was created at.
|
||||
/// </summary>
|
||||
public DateTime TimeCreated { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// The player-facing title for the vote.
|
||||
/// </summary>
|
||||
public required string Title { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// The user ID of the admin that created the vote.
|
||||
/// </summary>
|
||||
public Guid? InitiatorId { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// State of the vote.
|
||||
/// </summary>
|
||||
public CustomVoteState State { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// The playing-facing options the vote has.
|
||||
/// </summary>
|
||||
public List<CustomVoteLogOption>? Options { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// The round the vote was made in.
|
||||
/// </summary>
|
||||
public Round? Round { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// The admin that created the vote.
|
||||
/// </summary>
|
||||
public Player? Initiator { get; set; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// An option for a logged <see cref="CustomVoteLog"/>.
|
||||
/// </summary>
|
||||
public sealed class CustomVoteLogOption
|
||||
{
|
||||
/// <summary>
|
||||
/// The vote ID this option is part of.
|
||||
/// </summary>
|
||||
public int VoteId { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// The index of the option in the option list.
|
||||
/// </summary>
|
||||
public short OptionIdx { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// The player-facing text for this option.
|
||||
/// </summary>
|
||||
public required string Text { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// How many players voted for this option.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// This field is only populated for votes with state <see cref="CustomVoteState.Finished"/>.
|
||||
/// </remarks>
|
||||
public int VoteCount { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// The vote this option is part of.
|
||||
/// </summary>
|
||||
public CustomVoteLog? Vote { get; set; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Possible states for <see cref="CustomVoteLog"/> entries.
|
||||
/// </summary>
|
||||
public enum CustomVoteState : byte
|
||||
{
|
||||
/// <summary>
|
||||
/// The vote is still active (likely was just made).
|
||||
/// </summary>
|
||||
Active,
|
||||
|
||||
/// <summary>
|
||||
/// The vote finished and vote counts were written to database.
|
||||
/// </summary>
|
||||
Finished,
|
||||
|
||||
/// <summary>
|
||||
/// The vote was canceled, no vote counts have been written to database.
|
||||
/// </summary>
|
||||
Cancelled,
|
||||
}
|
||||
|
|
@ -49,6 +49,8 @@ namespace Content.Server.Database
|
|||
public DbSet<RoleWhitelist> RoleWhitelists { get; set; } = null!;
|
||||
public DbSet<BanTemplate> BanTemplate { get; set; } = null!;
|
||||
public DbSet<IPIntelCache> IPIntelCache { get; set; } = null!;
|
||||
public DbSet<CustomVoteLog> CustomVoteLog { get; set; } = null!;
|
||||
public DbSet<CustomVoteLogOption> CustomVoteLogOption { get; set; } = null!;
|
||||
|
||||
protected override void OnModelCreating(ModelBuilder modelBuilder)
|
||||
{
|
||||
|
|
@ -296,6 +298,7 @@ namespace Content.Server.Database
|
|||
.HasDefaultValue(HwidType.Legacy);
|
||||
|
||||
ModelBan.OnModelCreating(modelBuilder);
|
||||
ModelCustomVoteLog.OnModelCreating(modelBuilder);
|
||||
}
|
||||
|
||||
public virtual IQueryable<AdminLog> SearchLogs(IQueryable<AdminLog> query, string searchText)
|
||||
|
|
@ -595,6 +598,8 @@ namespace Content.Server.Database
|
|||
|
||||
public List<AdminLog> AdminLogs { get; set; } = default!;
|
||||
|
||||
public List<CustomVoteLog> CustomVoteLogs { get; set; } = default!;
|
||||
|
||||
[ForeignKey("Server")] public int ServerId { get; set; }
|
||||
public Server Server { get; set; } = default!;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1632,6 +1632,68 @@ INSERT INTO player_round (players_id, rounds_id) VALUES ({players[player]}, {id}
|
|||
|
||||
#endregion
|
||||
|
||||
#region Custom vote logging
|
||||
|
||||
public async Task<int> CustomVoteLogAdd(
|
||||
string title,
|
||||
int roundId,
|
||||
Guid? initiator,
|
||||
ImmutableArray<string> options)
|
||||
{
|
||||
await using var db = await GetDb();
|
||||
|
||||
var log = new CustomVoteLog
|
||||
{
|
||||
Title = title,
|
||||
RoundId = roundId,
|
||||
InitiatorId = initiator,
|
||||
State = CustomVoteState.Active,
|
||||
TimeCreated = DateTime.UtcNow,
|
||||
Options = options.Select((o, i) => new CustomVoteLogOption
|
||||
{
|
||||
Text = o,
|
||||
OptionIdx = (short)i,
|
||||
VoteCount = 0,
|
||||
})
|
||||
.ToList(),
|
||||
};
|
||||
|
||||
db.DbContext.CustomVoteLog.Add(log);
|
||||
await db.DbContext.SaveChangesAsync();
|
||||
|
||||
return log.Id;
|
||||
}
|
||||
|
||||
public async Task CustomVoteLogFinish(int voteId, ImmutableArray<int> voteCounts)
|
||||
{
|
||||
await using var db = await GetDb();
|
||||
|
||||
var log = await db.DbContext.CustomVoteLog
|
||||
.Include(cvl => cvl.Options)
|
||||
.SingleAsync(v => v.Id == voteId);
|
||||
|
||||
log.State = CustomVoteState.Finished;
|
||||
|
||||
for (var i = 0; i < log.Options!.Count; i++)
|
||||
{
|
||||
log.Options[i].VoteCount = voteCounts[i];
|
||||
}
|
||||
|
||||
await db.DbContext.SaveChangesAsync();
|
||||
}
|
||||
|
||||
public async Task CustomVoteLogCancel(int voteId)
|
||||
{
|
||||
await using var db = await GetDb();
|
||||
|
||||
var log = await db.DbContext.CustomVoteLog.SingleAsync(v => v.Id == voteId);
|
||||
log.State = CustomVoteState.Cancelled;
|
||||
|
||||
await db.DbContext.SaveChangesAsync();
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
public abstract Task SendNotification(DatabaseNotification notification);
|
||||
|
||||
// SQLite returns DateTime as Kind=Unspecified, Npgsql actually knows for sure it's Kind=Utc.
|
||||
|
|
|
|||
|
|
@ -329,6 +329,46 @@ namespace Content.Server.Database
|
|||
Task SendNotification(DatabaseNotification notification);
|
||||
|
||||
#endregion
|
||||
|
||||
#region Custom vote log
|
||||
|
||||
/// <summary>
|
||||
/// Log a new custom vote to the database.
|
||||
/// </summary>
|
||||
/// <param name="title">The player-facing title for the custom vote.</param>
|
||||
/// <param name="roundId">The round ID this vote was initiated.</param>
|
||||
/// <param name="initiator">The user ID of the admin that initiated the vote.</param>
|
||||
/// <param name="options">The player-facing contents of each vote option.</param>
|
||||
/// <remarks>
|
||||
/// The created vote is initially in the <see cref="CustomVoteState.Active"/> state.
|
||||
/// </remarks>
|
||||
/// <returns>
|
||||
/// The ID of the database entry,
|
||||
/// for subsequent calls to <see cref="CustomVoteLogFinish"/> or <see cref="CustomVoteLogCancel"/>.
|
||||
/// </returns>
|
||||
Task<int> CustomVoteLogAdd(string title, int roundId, NetUserId? initiator, ImmutableArray<string> options);
|
||||
|
||||
/// <summary>
|
||||
/// Mark a logged custom vote as finished.
|
||||
/// </summary>
|
||||
/// <param name="voteId">
|
||||
/// The database ID of the custom vote, as returned by <see cref="CustomVoteLogAdd"/>.
|
||||
/// </param>
|
||||
/// <param name="voteCounts">
|
||||
/// The counts each option received. The indexes are matched to the options given in
|
||||
/// <see cref="CustomVoteLogAdd"/>.
|
||||
/// </param>
|
||||
Task CustomVoteLogFinish(int voteId, ImmutableArray<int> voteCounts);
|
||||
|
||||
/// <summary>
|
||||
/// Mark a logged custom vote as canceled.
|
||||
/// </summary>
|
||||
/// <param name="voteId">
|
||||
/// The database ID of the custom vote, as returned by <see cref="CustomVoteLogAdd"/>.
|
||||
/// </param>
|
||||
Task CustomVoteLogCancel(int voteId);
|
||||
|
||||
#endregion
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
|
@ -997,6 +1037,28 @@ namespace Content.Server.Database
|
|||
return RunDbCommand(() => _db.SendNotification(notification));
|
||||
}
|
||||
|
||||
public Task<int> CustomVoteLogAdd(
|
||||
string title,
|
||||
int roundId,
|
||||
NetUserId? initiator,
|
||||
ImmutableArray<string> options)
|
||||
{
|
||||
DbWriteOpsMetric.Inc();
|
||||
return RunDbCommand(() => _db.CustomVoteLogAdd(title, roundId, initiator, options));
|
||||
}
|
||||
|
||||
public Task CustomVoteLogFinish(int voteId, ImmutableArray<int> voteCounts)
|
||||
{
|
||||
DbWriteOpsMetric.Inc();
|
||||
return RunDbCommand(() => _db.CustomVoteLogFinish(voteId, voteCounts));
|
||||
}
|
||||
|
||||
public Task CustomVoteLogCancel(int voteId)
|
||||
{
|
||||
DbWriteOpsMetric.Inc();
|
||||
return RunDbCommand(() => _db.CustomVoteLogCancel(voteId));
|
||||
}
|
||||
|
||||
private async void HandleDatabaseNotification(DatabaseNotification notification)
|
||||
{
|
||||
lock (_notificationHandlers)
|
||||
|
|
|
|||
|
|
@ -133,5 +133,10 @@ namespace Content.Server.GameTicking
|
|||
UpdateRoundFlow(frameTime);
|
||||
UpdateGameRules();
|
||||
}
|
||||
|
||||
public static int GetRoundId(IEntitySystemManager esm)
|
||||
{
|
||||
return esm.GetEntitySystemOrNull<GameTicker>()?.RoundId ?? 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -2,7 +2,9 @@ using System.Linq;
|
|||
using Content.Server.Administration;
|
||||
using Content.Server.Administration.Logs;
|
||||
using Content.Server.Chat.Managers;
|
||||
using Content.Server.Database;
|
||||
using Content.Server.Discord.WebhookMessages;
|
||||
using Content.Server.GameTicking;
|
||||
using Content.Server.Voting.Managers;
|
||||
using Content.Shared.Administration;
|
||||
using Content.Shared.CCVar;
|
||||
|
|
@ -71,12 +73,14 @@ namespace Content.Server.Voting
|
|||
[Dependency] private IChatManager _chatManager = default!;
|
||||
[Dependency] private VoteWebhooks _voteWebhooks = default!;
|
||||
[Dependency] private IConfigurationManager _cfg = default!;
|
||||
[Dependency] private IServerDbManager _dbManager = default!;
|
||||
[Dependency] private IEntitySystemManager _esm = default!;
|
||||
|
||||
private const int MaxArgCount = 10;
|
||||
|
||||
public override string Command => "customvote";
|
||||
|
||||
public override void Execute(IConsoleShell shell, string argStr, string[] args)
|
||||
public override async void Execute(IConsoleShell shell, string argStr, string[] args)
|
||||
{
|
||||
if (args.Length < 3 || args.Length > MaxArgCount)
|
||||
{
|
||||
|
|
@ -106,9 +110,15 @@ namespace Content.Server.Voting
|
|||
|
||||
var vote = _voteManager.CreateVote(options);
|
||||
|
||||
var voteLogId = await _dbManager.CustomVoteLogAdd(
|
||||
title,
|
||||
GameTicker.GetRoundId(_esm),
|
||||
shell.Player?.UserId,
|
||||
[..options.Options.Select(x => x.text)]);
|
||||
|
||||
var webhookState = _voteWebhooks.CreateWebhookIfConfigured(options, _cfg.GetCVar(CCVars.DiscordVoteWebhook));
|
||||
|
||||
vote.OnFinished += (_, eventArgs) =>
|
||||
vote.OnFinished += async (_, eventArgs) =>
|
||||
{
|
||||
if (eventArgs.Winner == null)
|
||||
{
|
||||
|
|
@ -123,11 +133,13 @@ namespace Content.Server.Voting
|
|||
}
|
||||
|
||||
_voteWebhooks.UpdateWebhookIfConfigured(webhookState, eventArgs);
|
||||
await _dbManager.CustomVoteLogFinish(voteLogId, [..eventArgs.Votes]);
|
||||
};
|
||||
|
||||
vote.OnCancelled += _ =>
|
||||
vote.OnCancelled += async _ =>
|
||||
{
|
||||
_voteWebhooks.UpdateCancelledWebhookIfConfigured(webhookState);
|
||||
await _dbManager.CustomVoteLogCancel(voteLogId);
|
||||
};
|
||||
}
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue