mirror of
https://github.com/JogmansonReal/Radicade.git
synced 2026-10-26 07:29:00 -07:00
293 lines
8.9 KiB
C#
293 lines
8.9 KiB
C#
using System.Text.Json;
|
|
using Microsoft.AspNetCore.Authorization;
|
|
using Microsoft.AspNetCore.Mvc;
|
|
using Microsoft.EntityFrameworkCore;
|
|
using RadicadeServer.Admin;
|
|
using RadicadeServer.Radicade.Discord;
|
|
using RadicadeServer.DB;
|
|
using RadicadeServer.Extensions;
|
|
using RadicadeServer.RecNet.Enums.Moderation;
|
|
using RadicadeServer.RecNet.Moderation;
|
|
using RadicadeServer.Discord.Webhooks;
|
|
|
|
namespace RadicadeServer.API.Moderation;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
public class ContentReportAPI : Controller
|
|
{
|
|
private readonly RecDBContext db;
|
|
|
|
public ContentReportAPI(RecDBContext dbContext) {
|
|
db = dbContext;
|
|
}
|
|
|
|
[HttpPost("api/inventions/v1/report"), Authorize]
|
|
[HttpPost("api/inventions/v2/report"), Authorize]
|
|
public Task<IActionResult> ReportInvention() => HandleReport("Invention",
|
|
new[] { "InventionId", "inventionId", "Id", "id", "TargetId", "targetId", "ReporteeId", "reporteeId" },
|
|
async targetId =>
|
|
{
|
|
var invention = await db.Inventions.AsNoTracking().FirstOrDefaultAsync(x => x.InventionId == targetId);
|
|
return invention == null ? null : (OwnerId: invention.CreatorPlayerId, TargetName: invention.Name);
|
|
});
|
|
|
|
[HttpPost("api/rooms/v1/report"), Authorize]
|
|
[HttpPost("api/rooms/v2/report"), Authorize]
|
|
public Task<IActionResult> ReportRoom() => HandleReport("Room",
|
|
new[] { "RoomId", "roomId", "Id", "id", "TargetId", "targetId", "ReporteeId", "reporteeId" },
|
|
async targetId =>
|
|
{
|
|
var room = await db.Rooms.AsNoTracking().FirstOrDefaultAsync(x => x.RoomId == targetId);
|
|
return room == null ? null : (OwnerId: room.CreatorAccountId, TargetName: room.Name);
|
|
});
|
|
|
|
[HttpPost("api/clubs/v1/report"), Authorize]
|
|
[HttpPost("api/clubs/v2/report"), Authorize]
|
|
[HttpPost("api/clubreporting/v1/report"), Authorize]
|
|
public Task<IActionResult> ReportClub() => HandleReport("Club",
|
|
new[] { "ClubId", "clubId", "Id", "id", "TargetId", "targetId", "ReporteeId", "reporteeId" },
|
|
_ => Task.FromResult<(long OwnerId, string TargetName)?>(null));
|
|
|
|
[HttpPost("api/board/v1/report"), Authorize]
|
|
[HttpPost("api/board/v2/report"), Authorize]
|
|
[HttpPost("api/communityboard/v1/report"), Authorize]
|
|
[HttpPost("api/communityboard/v2/report"), Authorize]
|
|
public Task<IActionResult> ReportBoard() => HandleReport("Board",
|
|
new[] { "BoardId", "boardId", "PostId", "postId", "CommentId", "commentId", "Id", "id", "TargetId", "targetId" },
|
|
_ => Task.FromResult<(long OwnerId, string TargetName)?>(null));
|
|
|
|
[HttpPost("api/playerevents/v2/report"), Authorize]
|
|
[HttpPost("api/events/v1/report"), Authorize]
|
|
[HttpPost("api/events/v2/report"), Authorize]
|
|
public Task<IActionResult> ReportEvent() => HandleReport("Event",
|
|
new[] { "EventId", "eventId", "PlayerEventId", "playerEventId", "Id", "id", "TargetId", "targetId", "ReporteeId", "reporteeId" },
|
|
async targetId =>
|
|
{
|
|
var ev = await db.PlayerEvents.AsNoTracking().FirstOrDefaultAsync(x => x.PlayerEventId == targetId);
|
|
return ev == null ? null : (OwnerId: ev.CreatorPlayerId, TargetName: ev.Name);
|
|
});
|
|
|
|
private async Task<IActionResult> HandleReport(
|
|
string reportType,
|
|
string[] idFieldNames,
|
|
Func<long, Task<(long OwnerId, string TargetName)?>> resolveTarget)
|
|
{
|
|
var reporterId = User.GetUserId();
|
|
if (reporterId == null) return Unauthorized();
|
|
|
|
var fields = await ReadFieldsAsync();
|
|
|
|
var targetId = GetLong(fields, idFieldNames);
|
|
|
|
long reportedId = 0;
|
|
string targetName = "";
|
|
if (targetId.HasValue)
|
|
{
|
|
var resolved = await resolveTarget(targetId.Value);
|
|
if (resolved.HasValue)
|
|
{
|
|
reportedId = resolved.Value.OwnerId;
|
|
targetName = resolved.Value.TargetName;
|
|
}
|
|
else
|
|
{
|
|
|
|
|
|
|
|
targetName = $"{reportType} {targetId.Value}";
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
return await SubmitReport(reportType, targetId, targetName, reportedId, reporterId.Value, fields);
|
|
}
|
|
|
|
[HttpPost("api/customAvatarItems/v1/{customAvatarItemId:guid}/report"), Authorize]
|
|
public async Task<IActionResult> ReportCustomAvatarItem(Guid customAvatarItemId)
|
|
{
|
|
var reporterId = User.GetUserId();
|
|
if (reporterId == null) return Unauthorized();
|
|
|
|
var fields = await ReadFieldsAsync();
|
|
|
|
var item = await db.CustomAvatarItems.AsNoTracking()
|
|
.FirstOrDefaultAsync(x => x.CustomAvatarItemId == customAvatarItemId);
|
|
|
|
return await SubmitReport(
|
|
"CustomAvatarItem",
|
|
null,
|
|
item?.Name ?? $"Custom Avatar Item {customAvatarItemId}",
|
|
item?.CreatorAccountId ?? 0,
|
|
reporterId.Value,
|
|
fields);
|
|
}
|
|
|
|
private async Task<IActionResult> SubmitReport(
|
|
string reportType,
|
|
long? targetId,
|
|
string targetName,
|
|
long reportedId,
|
|
long reporterId,
|
|
Dictionary<string, string> fields)
|
|
{
|
|
var reason = GetField(fields, "Reason", "reason", "ReportReason", "reportReason") ?? "";
|
|
var details = GetField(fields, "Details", "details", "Description", "description", "Message", "message", "Text", "text") ?? "";
|
|
var category = ParseCategory(fields);
|
|
var roomId = GetLong(fields, "RoomId", "roomId") ?? 0;
|
|
|
|
if (reportedId > 0 && reportedId != reporterId)
|
|
{
|
|
var reputation = await db.Reputations.FirstOrDefaultAsync(x => x.AccountId == reportedId);
|
|
if (reputation == null)
|
|
{
|
|
reputation = new Reputation { AccountId = reportedId };
|
|
await db.Reputations.AddAsync(reputation);
|
|
}
|
|
reputation.ReportCount += 1;
|
|
await db.SaveChangesAsync();
|
|
}
|
|
|
|
AdminPanelAPI.AddModerationReport(reporterId, reportedId, reason, details, category, roomId,
|
|
reportType, targetId, targetName);
|
|
|
|
ReportDiscord.SendAsync(WebhookConfig.ReportsUrl, $"🚩 {reportType} Report",
|
|
new DiscordPayload
|
|
{
|
|
Title = $"{reportType} Report",
|
|
ThumbnailUrl = WebhookImages.ForReport(reportType) ?? "",
|
|
Color = 0xF0B232,
|
|
Fields = new List<DiscordField>
|
|
{
|
|
new() { Name = "Report Type", Value = reportType, Inline = true },
|
|
new() { Name = "Reporter", Value = reporterId.ToString(), Inline = true },
|
|
new() { Name = "Reported Player", Value = reportedId > 0 ? reportedId.ToString() : "Unknown", Inline = true },
|
|
new() { Name = "Target", Value = targetId.HasValue ? $"{targetName} ({targetId})" : targetName, Inline = true },
|
|
new() { Name = "Category", Value = category.ToString(), Inline = true },
|
|
new() { Name = "Room", Value = roomId.ToString(), Inline = true },
|
|
new() { Name = "Reason", Value = reason, Inline = false },
|
|
new() { Name = "Details", Value = details, Inline = false }
|
|
},
|
|
TimestampUtc = DateTime.UtcNow.ToString("O")
|
|
});
|
|
|
|
return Ok(new OkResponse { Success = true });
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
private async Task<Dictionary<string, string>> ReadFieldsAsync()
|
|
{
|
|
var fields = new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase);
|
|
|
|
foreach (var kv in Request.Query)
|
|
fields[kv.Key] = kv.Value.ToString();
|
|
|
|
if (Request.HasFormContentType)
|
|
{
|
|
var form = await Request.ReadFormAsync();
|
|
foreach (var kv in form)
|
|
fields[kv.Key] = kv.Value.ToString();
|
|
return fields;
|
|
}
|
|
|
|
if (Request.ContentLength is not > 0) return fields;
|
|
|
|
try
|
|
{
|
|
using var reader = new StreamReader(Request.Body);
|
|
var body = await reader.ReadToEndAsync();
|
|
if (string.IsNullOrWhiteSpace(body)) return fields;
|
|
|
|
var trimmed = body.TrimStart();
|
|
if (trimmed.StartsWith("{", StringComparison.Ordinal))
|
|
{
|
|
using var doc = JsonDocument.Parse(body);
|
|
if (doc.RootElement.ValueKind == JsonValueKind.Object)
|
|
{
|
|
foreach (var prop in doc.RootElement.EnumerateObject())
|
|
{
|
|
fields[prop.Name] = prop.Value.ValueKind == JsonValueKind.String
|
|
? prop.Value.GetString() ?? ""
|
|
: prop.Value.ToString();
|
|
}
|
|
}
|
|
}
|
|
else if (trimmed.Contains('='))
|
|
{
|
|
|
|
foreach (var pair in body.Split('&', StringSplitOptions.RemoveEmptyEntries))
|
|
{
|
|
var eq = pair.IndexOf('=');
|
|
if (eq < 0)
|
|
{
|
|
fields[Uri.UnescapeDataString(pair)] = "";
|
|
continue;
|
|
}
|
|
var key = Uri.UnescapeDataString(pair.Substring(0, eq));
|
|
var value = Uri.UnescapeDataString(pair.Substring(eq + 1));
|
|
fields[key] = value;
|
|
}
|
|
}
|
|
}
|
|
catch
|
|
{
|
|
|
|
|
|
}
|
|
|
|
return fields;
|
|
}
|
|
|
|
private static string? GetField(Dictionary<string, string> fields, params string[] names)
|
|
{
|
|
foreach (var name in names)
|
|
{
|
|
if (fields.TryGetValue(name, out var value) && !string.IsNullOrWhiteSpace(value))
|
|
return value;
|
|
}
|
|
return null;
|
|
}
|
|
|
|
private static long? GetLong(Dictionary<string, string> fields, params string[] names)
|
|
{
|
|
var raw = GetField(fields, names);
|
|
if (raw == null) return null;
|
|
if (long.TryParse(raw, out var value)) return value;
|
|
|
|
|
|
var digits = new string(raw.Where(char.IsDigit).ToArray());
|
|
return long.TryParse(digits, out value) ? value : null;
|
|
}
|
|
|
|
private static ReportCategory ParseCategory(Dictionary<string, string> fields)
|
|
{
|
|
var raw = GetField(fields, "ReportCategory", "reportCategory", "Category", "category", "Reason", "reason", "ReportReason", "reportReason");
|
|
if (raw == null) return ReportCategory.Misc;
|
|
|
|
if (int.TryParse(raw, out var intValue) && Enum.IsDefined(typeof(ReportCategory), intValue))
|
|
return (ReportCategory)intValue;
|
|
|
|
if (Enum.TryParse<ReportCategory>(raw, ignoreCase: true, out var parsed))
|
|
return parsed;
|
|
|
|
return ReportCategory.Misc;
|
|
}
|
|
}
|