mirror of
https://github.com/DorkSquadRR/DorkNet.git
synced 2026-10-26 07:28:45 -07:00
feat: add 3D Charades word lists management and profanity filter toggle
- Implemented Charades word lists management in the admin UI, allowing for unlimited named word lists with difficulty settings. - Added functionality to bind live card slots to specific word lists, with automatic fallback to built-in lists. - Introduced a profanity filter toggle in server settings, enabling or disabling the server-side profanity filter. - Updated database schema to support new features, including additional columns for profanity filter status and charades slot bindings. - Enhanced documentation to reflect new features and their usage.
This commit is contained in:
@@ -1,16 +1,32 @@
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using DorkNet.Server.Services;
|
||||
|
||||
namespace DorkNet.Server.Controllers.API.Activities;
|
||||
|
||||
/// <summary>
|
||||
/// api.{rec.net,localhost}/api/activities/* — per-activity content
|
||||
/// (charades words, party-game prompts, etc.). The 2020 watch
|
||||
/// fetches these as a static list at room-join time; we ship an
|
||||
/// empty list so the activity's bundled defaults kick in.
|
||||
/// (charades words, party-game prompts, etc.).
|
||||
///
|
||||
/// The March 2023 watch fetches a 3D Charades deck at card-box spawn from
|
||||
/// <c>api/activities/charades/v1/words/{source}</c>, where <c>{source}</c>
|
||||
/// is one of three baked <c>CardBox.cardSource</c> slots — <c>Charades</c>,
|
||||
/// <c>CharadesAprilFoolsDay</c>, or <c>Icebreakers</c> (verified in the
|
||||
/// 2023.03.21 il2cpp dump). We resolve the admin-bound word list for the
|
||||
/// requested slot via <see cref="CharadesWordListService"/> and return the
|
||||
/// wire array of <c>{ EN_US, Difficulty }</c>. The older December route
|
||||
/// (no <c>{source}</c>) maps to the default Charades slot.
|
||||
/// </summary>
|
||||
[ApiController]
|
||||
public class ActivitiesController : ControllerBase
|
||||
public class ActivitiesController(CharadesWordListService charades) : ControllerBase
|
||||
{
|
||||
[HttpGet("api/activities/charades/v1/words/{source}")]
|
||||
public async Task<IActionResult> CharadesWords(string source)
|
||||
=> Ok(await charades.ResolveWireWordsAsync(source));
|
||||
|
||||
/// <summary>December 2020 shape — no source segment. Serves the default
|
||||
/// Charades slot so the older client still gets a populated deck.</summary>
|
||||
[HttpGet("api/activities/charades/v1/words")]
|
||||
public IActionResult CharadesWords() => Ok(Array.Empty<object>());
|
||||
public async Task<IActionResult> CharadesWordsDefault()
|
||||
=> Ok(await charades.ResolveWireWordsAsync(
|
||||
nameof(CharadesWordListService.CharadesSlot.Charades)));
|
||||
}
|
||||
|
||||
@@ -10,10 +10,29 @@ namespace DorkNet.Server.Controllers.API.Sanitize;
|
||||
/// invention name / chat message to be submitted. Delegates to
|
||||
/// <see cref="ProfanityFilter"/> for the actual scrubbing. Response
|
||||
/// shape: <c>{Text (purified), IsClean (bool)}</c>.
|
||||
///
|
||||
/// When the admin has flipped
|
||||
/// <see cref="Data.Entities.ServerSettingsEntity.ProfanityFilterDisabled"/>,
|
||||
/// every route short-circuits to "clean" — input is returned unmodified
|
||||
/// and purity checks always pass — so nothing gets censored.
|
||||
/// </summary>
|
||||
[ApiController]
|
||||
public class SanitizeController(ILogger<SanitizeController> logger) : ControllerBase
|
||||
public class SanitizeController(
|
||||
ILogger<SanitizeController> logger,
|
||||
ServerSettingsService settings) : ControllerBase
|
||||
{
|
||||
/// <summary>Purify unless the filter is admin-disabled, in which case
|
||||
/// the input is returned untouched.</summary>
|
||||
private async Task<string> PurifyAsync(string input)
|
||||
=> await settings.IsProfanityFilterDisabledAsync()
|
||||
? input
|
||||
: ProfanityFilter.Purify(input);
|
||||
|
||||
/// <summary>Clean-check unless the filter is admin-disabled, in which
|
||||
/// case everything is treated as clean.</summary>
|
||||
private async Task<bool> IsCleanAsync(string input)
|
||||
=> await settings.IsProfanityFilterDisabledAsync() || ProfanityFilter.IsClean(input);
|
||||
|
||||
[HttpPost("api/sanitize/v1")]
|
||||
[HttpPost("api/sanitize/v1/text")]
|
||||
public async Task<IActionResult> Sanitize()
|
||||
@@ -73,7 +92,7 @@ public class SanitizeController(ILogger<SanitizeController> logger) : Controller
|
||||
// ASP.NET Core's StringOutputFormatter doesn't serialize as
|
||||
// text/plain (raw string sans quotes), which the watch then sees
|
||||
// as an empty cleanVersion after LitJson decode.
|
||||
var purified = ProfanityFilter.Purify(text);
|
||||
var purified = await PurifyAsync(text);
|
||||
var json = System.Text.Json.JsonSerializer.Serialize(purified);
|
||||
logger.LogInformation("[sanitize] input=\"{Input}\" purified=\"{Purified}\" json={Json}", text, purified, json);
|
||||
return Content(json, "application/json");
|
||||
@@ -95,7 +114,7 @@ public class SanitizeController(ILogger<SanitizeController> logger) : Controller
|
||||
/// <c>{Text}</c> with profanity replaced. Wire shape matches
|
||||
/// <c>StringSanitization.PurifyString</c>.</summary>
|
||||
[HttpPost("api/sanitize/v1/purifyString")]
|
||||
public IActionResult PurifyString(
|
||||
public async Task<IActionResult> PurifyString(
|
||||
[FromBody] SanitizeBody? body,
|
||||
[FromForm(Name = "Text")] string? textForm,
|
||||
[FromForm(Name = "Input")] string? inputForm,
|
||||
@@ -104,28 +123,28 @@ public class SanitizeController(ILogger<SanitizeController> logger) : Controller
|
||||
{
|
||||
var input = body?.Text ?? body?.Input ?? body?.Value
|
||||
?? textForm ?? inputForm ?? valueForm ?? textQuery ?? string.Empty;
|
||||
return Ok(new { Text = ProfanityFilter.Purify(input) });
|
||||
return Ok(new { Text = await PurifyAsync(input) });
|
||||
}
|
||||
|
||||
/// <summary>POST <c>api/sanitize/v1/requestIsStringPure</c> —
|
||||
/// returns a bare JSON bool. Watch's <c>ExpectPrimitiveResponse</c>
|
||||
/// rejects object bodies here.</summary>
|
||||
[HttpPost("api/sanitize/v1/requestIsStringPure")]
|
||||
public IActionResult IsStringPure(
|
||||
public async Task<IActionResult> IsStringPure(
|
||||
[FromBody] SanitizeBody? body,
|
||||
[FromForm(Name = "Text")] string? textForm,
|
||||
[FromQuery(Name = "text")] string? textQuery)
|
||||
{
|
||||
var input = body?.Text ?? body?.Input ?? body?.Value
|
||||
?? textForm ?? textQuery ?? string.Empty;
|
||||
return Ok(ProfanityFilter.IsClean(input));
|
||||
return Ok(await IsCleanAsync(input));
|
||||
}
|
||||
|
||||
/// <summary>POST <c>api/sanitize/v1/isPure</c> — 2023 route used
|
||||
/// by CV2 maker pen text such as port names. It imports an object
|
||||
/// field named <c>IsPure</c>.</summary>
|
||||
[HttpPost("api/sanitize/v1/isPure")]
|
||||
public IActionResult IsPureV1(
|
||||
public async Task<IActionResult> IsPureV1(
|
||||
[FromBody] SanitizeBody? body,
|
||||
[FromForm(Name = "Text")] string? textForm,
|
||||
[FromForm(Name = "Input")] string? inputForm,
|
||||
@@ -134,7 +153,7 @@ public class SanitizeController(ILogger<SanitizeController> logger) : Controller
|
||||
{
|
||||
var input = body?.Text ?? body?.Input ?? body?.Value
|
||||
?? textForm ?? inputForm ?? valueForm ?? textQuery ?? string.Empty;
|
||||
var isPure = ProfanityFilter.IsClean(input);
|
||||
var isPure = await IsCleanAsync(input);
|
||||
logger.LogInformation(
|
||||
"[sanitize] isPure context={Context} intent={Intent} ruleset={Ruleset} valueLength={ValueLength} isPure={IsPure}",
|
||||
body?.Context ?? string.Empty,
|
||||
@@ -151,13 +170,13 @@ public class SanitizeController(ILogger<SanitizeController> logger) : Controller
|
||||
/// used by room/settings forms. Its importer reads an <c>IsPure</c>
|
||||
/// object field, unlike the newer bare-bool route above.</summary>
|
||||
[HttpPost("api/sanitize/isPure")]
|
||||
public IActionResult IsPure(
|
||||
public async Task<IActionResult> IsPure(
|
||||
[FromBody] SanitizeBody? body,
|
||||
[FromForm(Name = "Text")] string? textForm,
|
||||
[FromQuery(Name = "text")] string? textQuery)
|
||||
{
|
||||
var input = body?.Text ?? body?.Input ?? body?.Value
|
||||
?? textForm ?? textQuery ?? string.Empty;
|
||||
return Ok(new { IsPure = ProfanityFilter.IsClean(input) });
|
||||
return Ok(new { IsPure = await IsCleanAsync(input) });
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user