mirror of
https://github.com/JogmansonReal/Radicade.git
synced 2026-10-25 15:09:26 -07:00
724 lines
24 KiB
C#
724 lines
24 KiB
C#
using Microsoft.AspNetCore.Authorization;
|
|
using Microsoft.AspNetCore.Mvc;
|
|
using Microsoft.EntityFrameworkCore;
|
|
using System.Text;
|
|
using System.Text.Json.Nodes;
|
|
using RadicadeServer.API.Notifications;
|
|
using RadicadeServer.DB;
|
|
using RadicadeServer.Extensions;
|
|
using RadicadeServer.RecNet.Enums;
|
|
using RadicadeServer.RecNet.Enums.Rooms;
|
|
using RadicadeServer.RecNet.Notifications;
|
|
using RadicadeServer.RecNet.Responses;
|
|
using RadicadeServer.RecNet.Rooms;
|
|
using RadicadeServer.Utils;
|
|
|
|
|
|
namespace RadicadeServer.RoomServer
|
|
{
|
|
[Route("rooms/rooms/")]
|
|
public class V2_RoomsAPI(RecDBContext db) : Controller
|
|
{
|
|
[HttpGet("{id:long}/similar"), Authorize]
|
|
public async Task<IActionResult> GetSimilarRooms(long id, [FromQuery] int skip = 0, [FromQuery] int take = 100)
|
|
{
|
|
var room = await db.Rooms.FirstOrDefaultAsync(x => x.RoomId == id);
|
|
if (room == null) return Ok(EmptyPage());
|
|
|
|
var userId = User.GetUserId();
|
|
skip = Math.Max(skip, 0);
|
|
take = Math.Clamp(take, 1, 100);
|
|
|
|
var ownTags = StringTools.ReadTags(room.Tags)
|
|
.Select(t => t.Tag?.Trim().ToLowerInvariant())
|
|
.Where(t => !string.IsNullOrEmpty(t))
|
|
.ToHashSet();
|
|
|
|
var candidates = await db.Rooms
|
|
.Where(x => x.RoomId != id && x.Accessibility == Accessibility.Public)
|
|
.ToListAsync();
|
|
|
|
var scored = new List<(RoomData Room, int Score, int Visits)>();
|
|
foreach (var candidate in candidates)
|
|
{
|
|
if (!await RoomUtils.CanSeeRoom(candidate, userId)) continue;
|
|
|
|
int score;
|
|
if (ownTags.Count == 0)
|
|
{
|
|
score = candidate.CreatorAccountId == room.CreatorAccountId ? 1 : 0;
|
|
}
|
|
else
|
|
{
|
|
score = StringTools.ReadTags(candidate.Tags)
|
|
.Count(t => ownTags.Contains(t.Tag?.Trim().ToLowerInvariant()));
|
|
}
|
|
|
|
if (score <= 0) continue;
|
|
|
|
var stats = await db.RoomStats.FirstOrDefaultAsync(x => x.RoomId == candidate.RoomId);
|
|
scored.Add((candidate, score, stats?.VisitCount ?? 0));
|
|
}
|
|
|
|
var ordered = scored
|
|
.OrderByDescending(x => x.Score)
|
|
.ThenByDescending(x => x.Visits)
|
|
.Select(x => x.Room)
|
|
.ToList();
|
|
|
|
var picked = ordered.Skip(skip).Take(take).ToList();
|
|
var details = await RoomUtils.BulkConvertRoomDataToRoomDetail(picked);
|
|
|
|
return Ok(new RoomSearchResult { TotalResults = ordered.Count, Results = details });
|
|
}
|
|
|
|
private static RoomSearchResult EmptyPage() => new() { TotalResults = 0, Results = new List<RoomDetail>() };
|
|
|
|
[HttpGet("{id:long}/experience")]
|
|
public IActionResult GetRoomExperience(long id)
|
|
=> Ok(new { Enabled = false, DailyLimit = 1000 });
|
|
|
|
[HttpGet("{id:long}/experience/player")]
|
|
public IActionResult GetRoomExperienceForPlayer(long id)
|
|
=> Ok(Array.Empty<object>());
|
|
|
|
[HttpPut("{id}/description"), Authorize]
|
|
public async Task<Success> SetDescription(long? id, string description)
|
|
{
|
|
var userId = User.GetUserId();
|
|
if (userId == null) return new Success(false, "Invalid auth", null);
|
|
|
|
var canModify = await MatchmakingUtils.IsAtLeastRole((long)userId, (long)id!, RoleType.CoOwner);
|
|
if (!canModify) return Success.Error("Insufficient permissions");
|
|
|
|
var room = await db.Rooms.FirstOrDefaultAsync(x => x.RoomId == id);
|
|
if (room == null) return Success.Error("Room not found");
|
|
|
|
if (!description.IsPure()) return new Success(false, "Description contains offensive language.", null);
|
|
|
|
room.Description = description;
|
|
|
|
await db.SaveChangesAsync();
|
|
|
|
var fullRoom = await RoomUtils.GetRoom((long)id!);
|
|
await Notify.SendNotify((long)userId, new Notification { Id = "RoomUpdate", Msg = fullRoom });
|
|
|
|
return new Success(true, null, fullRoom);
|
|
}
|
|
|
|
[HttpPut("{id}/name"), Authorize]
|
|
public async Task<Success> SetName(long? id, string name)
|
|
{
|
|
var userId = User.GetUserId();
|
|
if (userId == null) return new Success(false, "Invalid auth", null);
|
|
|
|
var canModify = await MatchmakingUtils.IsAtLeastRole((long)userId, (long)id!, RoleType.CoOwner);
|
|
if (!canModify) return Success.Error("Insufficient permissions");
|
|
|
|
var room = await db.Rooms.FirstOrDefaultAsync(x => x.RoomId == id);
|
|
if (room == null) return Success.Error("Room not found");
|
|
|
|
if (!name.IsPure()) return new Success(false, "Name contains offensive language.", null);
|
|
|
|
var sameRoomWithName = await db.Rooms.FirstOrDefaultAsync(x => x.Name == name);
|
|
if (sameRoomWithName != null) return new Success(false, "A room already exists with that name.", null);
|
|
|
|
room.Name = name;
|
|
|
|
await db.SaveChangesAsync();
|
|
|
|
var fullRoom = await RoomUtils.GetRoom((long)id!);
|
|
await Notify.SendNotify((long)userId, new Notification { Id = "RoomUpdate", Msg = fullRoom });
|
|
|
|
return new Success(true, null, fullRoom);
|
|
}
|
|
|
|
[HttpPut("{id}/warning"), Authorize]
|
|
public async Task<Success> SetWarning(long? id, WarningMask? warningMask, string? customWarning)
|
|
{
|
|
var userId = User.GetUserId();
|
|
if (userId == null) return new Success(false, "Invalid auth", null);
|
|
|
|
var canModify = await MatchmakingUtils.IsAtLeastRole((long)userId, (long)id!, RoleType.CoOwner);
|
|
if (!canModify) return Success.Error("Insufficient permissions");
|
|
|
|
var room = await db.Rooms.FirstOrDefaultAsync(x => x.RoomId == id);
|
|
if (room == null) return Success.Error("Room not found");
|
|
|
|
if (warningMask != null)
|
|
room.WarningMask = warningMask.Value;
|
|
if (customWarning != null)
|
|
room.CustomWarning = customWarning;
|
|
|
|
await db.SaveChangesAsync();
|
|
|
|
var fullRoom = await RoomUtils.GetRoom((long)id!);
|
|
await Notify.SendNotify((long)userId, new Notification { Id = "RoomUpdate", Msg = fullRoom });
|
|
|
|
return new Success(true, null, fullRoom);
|
|
}
|
|
|
|
[HttpPut("{id}/cloning"), Authorize]
|
|
public async Task<Success> SetAllowedCloning(long? id, bool cloningAllowed)
|
|
{
|
|
var userId = User.GetUserId();
|
|
if (userId == null) return new Success(false, "Invalid auth", null);
|
|
|
|
var canModify = await MatchmakingUtils.IsAtLeastRole((long)userId, (long)id!, RoleType.CoOwner);
|
|
if (!canModify) return Success.Error("Insufficient permissions");
|
|
|
|
var room = await db.Rooms.FirstOrDefaultAsync(x => x.RoomId == id);
|
|
if (room == null) return Success.Error("Room not found");
|
|
|
|
room.CloningAllowed = cloningAllowed;
|
|
|
|
await db.SaveChangesAsync();
|
|
|
|
var fullRoom = await RoomUtils.GetRoom((long)id!);
|
|
await Notify.SendNotify((long)userId, new Notification { Id = "RoomUpdate", Msg = fullRoom });
|
|
|
|
return new Success(true, null, fullRoom);
|
|
}
|
|
|
|
[HttpPut("{id}/restrictions"), Authorize]
|
|
public async Task<Success> SetRestrictions(long? id, bool supportsScreens, bool supportsWalkVR, bool supportsTeleportVR, bool supportsJuniors)
|
|
{
|
|
var userId = User.GetUserId();
|
|
if (userId == null) return new Success(false, "Invalid auth", null);
|
|
|
|
var canModify = await MatchmakingUtils.IsAtLeastRole((long)userId, (long)id!, RoleType.CoOwner);
|
|
if (!canModify) return Success.Error("Insufficient permissions");
|
|
|
|
var room = await db.Rooms.FirstOrDefaultAsync(x => x.RoomId == id);
|
|
if (room == null) return Success.Error("Room not found");
|
|
|
|
room.SupportsTeleportVR = supportsTeleportVR;
|
|
room.SupportsWalkVR = supportsWalkVR;
|
|
room.SupportsScreens = supportsScreens;
|
|
room.SupportsJuniors = supportsJuniors;
|
|
|
|
await db.SaveChangesAsync();
|
|
|
|
var fullRoom = await RoomUtils.GetRoom((long)id!);
|
|
await Notify.SendNotify((long)userId, new Notification { Id = "RoomUpdate", Msg = fullRoom });
|
|
|
|
return new Success(true, null, fullRoom);
|
|
}
|
|
|
|
[HttpPut("{id}/automute"), Authorize]
|
|
public async Task<Success> SetDisableAutoMute(long? id, bool disable)
|
|
{
|
|
var userId = User.GetUserId();
|
|
if (userId == null) return new Success(false, "Invalid auth", null);
|
|
|
|
var canModify = await MatchmakingUtils.IsAtLeastRole((long)userId, (long)id!, RoleType.CoOwner);
|
|
if (!canModify) return Success.Error("Insufficient permissions");
|
|
|
|
var room = await db.Rooms.FirstOrDefaultAsync(x => x.RoomId == id);
|
|
if (room == null) return Success.Error("Room not found");
|
|
|
|
room.DisableMicAutoMute = disable;
|
|
|
|
await db.SaveChangesAsync();
|
|
|
|
var fullRoom = await RoomUtils.GetRoom((long)id!);
|
|
await Notify.SendNotify((long)userId, new Notification { Id = "RoomUpdate", Msg = fullRoom });
|
|
|
|
return new Success(true, null, fullRoom);
|
|
}
|
|
|
|
[HttpPut("{id}/comments"), Authorize]
|
|
public async Task<Success> SetDisableComments(long? id, bool disable)
|
|
{
|
|
var userId = User.GetUserId();
|
|
if (userId == null) return new Success(false, "Invalid auth", null);
|
|
|
|
var canModify = await MatchmakingUtils.IsAtLeastRole((long)userId, (long)id!, RoleType.CoOwner);
|
|
if (!canModify) return Success.Error("Insufficient permissions");
|
|
|
|
var room = await db.Rooms.FirstOrDefaultAsync(x => x.RoomId == id);
|
|
if (room == null) return Success.Error("Room not found");
|
|
|
|
room.DisableRoomComments = disable;
|
|
|
|
await db.SaveChangesAsync();
|
|
|
|
var fullRoom = await RoomUtils.GetRoom((long)id!);
|
|
await Notify.SendNotify((long)userId, new Notification { Id = "RoomUpdate", Msg = fullRoom });
|
|
|
|
return new Success(true, null, fullRoom);
|
|
}
|
|
|
|
[HttpPut("{id}/voice_chat_encryption"), Authorize]
|
|
public async Task<Success> SetEncryptVoiceChat(long? id, bool encryptVoiceChat)
|
|
{
|
|
var userId = User.GetUserId();
|
|
if (userId == null) return new Success(false, "Invalid auth", null);
|
|
|
|
var canModify = await MatchmakingUtils.IsAtLeastRole((long)userId, (long)id!, RoleType.CoOwner);
|
|
if (!canModify) return Success.Error("Insufficient permissions");
|
|
|
|
var room = await db.Rooms.FirstOrDefaultAsync(x => x.RoomId == id);
|
|
if (room == null) return Success.Error("Room not found");
|
|
|
|
room.EncryptVoiceChat = encryptVoiceChat;
|
|
|
|
await db.SaveChangesAsync();
|
|
|
|
var fullRoom = await RoomUtils.GetRoom((long)id!);
|
|
await Notify.SendNotify((long)userId, new Notification { Id = "RoomUpdate", Msg = fullRoom });
|
|
|
|
return new Success(true, null, fullRoom);
|
|
}
|
|
|
|
[HttpPut("{id}/allow_new_users"), Authorize]
|
|
public async Task<Success> SetAllowNewUsers(long? id, bool allowNewUsers)
|
|
{
|
|
var userId = User.GetUserId();
|
|
if (userId == null) return new Success(false, "Invalid auth", null);
|
|
|
|
var canModify = await MatchmakingUtils.IsAtLeastRole((long)userId, (long)id!, RoleType.CoOwner);
|
|
if (!canModify) return Success.Error("Insufficient permissions");
|
|
|
|
var room = await db.Rooms.FirstOrDefaultAsync(x => x.RoomId == id);
|
|
if (room == null) return Success.Error("Room not found");
|
|
|
|
var fullRoom = await RoomUtils.GetRoom((long)id!);
|
|
await Notify.SendNotify((long)userId, new Notification { Id = "RoomUpdate", Msg = fullRoom });
|
|
|
|
return new Success(true, null, fullRoom);
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
[HttpGet("{id}/bans"), Authorize]
|
|
public async Task<List<RoomBan>> GetRoomBans(long? id)
|
|
{
|
|
var userId = User.GetUserId();
|
|
if (userId == null || id == null) return new List<RoomBan>();
|
|
|
|
var room = await db.Rooms.FirstOrDefaultAsync(x => x.RoomId == id);
|
|
if (room == null) return new List<RoomBan>();
|
|
|
|
var canModify = await MatchmakingUtils.CanModerateRoom((long)userId, (long)id!);
|
|
if (!canModify) return new List<RoomBan>();
|
|
|
|
return await db.RoomBans
|
|
.Where(x => x.roomId == id && (!x.expiresAt.HasValue || x.expiresAt.Value > DateTime.UtcNow))
|
|
.OrderByDescending(x => x.bannedAt)
|
|
.ToListAsync();
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
[HttpGet("{id}/bans/{bannedPlayerId}"), Authorize]
|
|
public async Task<RoomBan?> GetRoomBan(long? id, long bannedPlayerId)
|
|
{
|
|
var userId = User.GetUserId();
|
|
if (userId == null || id == null) return null;
|
|
|
|
var room = await db.Rooms.FirstOrDefaultAsync(x => x.RoomId == id);
|
|
if (room == null) return null;
|
|
|
|
|
|
if (bannedPlayerId != (long)userId)
|
|
{
|
|
var canModify = await MatchmakingUtils.CanModerateRoom((long)userId, (long)id!);
|
|
if (!canModify) return null;
|
|
}
|
|
|
|
return await db.RoomBans
|
|
.FirstOrDefaultAsync(x => x.roomId == id && x.bannedPlayerId == bannedPlayerId &&
|
|
(!x.expiresAt.HasValue || x.expiresAt.Value > DateTime.UtcNow));
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
[HttpPost("{id}/bans/import"), Authorize]
|
|
public async Task<Success> ImportRoomBans(long? id)
|
|
{
|
|
var userId = User.GetUserId();
|
|
if (userId == null || id == null) return new Success(false, "Invalid auth", null);
|
|
|
|
var room = await db.Rooms.FirstOrDefaultAsync(x => x.RoomId == id);
|
|
if (room == null) return Success.Error("Room not found");
|
|
|
|
var canModify = await MatchmakingUtils.CanModerateRoom((long)userId, (long)id!);
|
|
if (!canModify) return Success.Error("Insufficient permissions");
|
|
|
|
var playerIds = new List<long>();
|
|
string rawBody = "";
|
|
|
|
try
|
|
{
|
|
if (Request.HasFormContentType)
|
|
{
|
|
var form = await Request.ReadFormAsync();
|
|
foreach (var key in new[] { "playerIds", "PlayerIds", "ids", "Ids", "bannedPlayerIds", "BannedPlayerIds", "players", "Players" })
|
|
if (form.TryGetValue(key, out var values))
|
|
foreach (var value in values)
|
|
foreach (var part in (value ?? string.Empty).Split(',', StringSplitOptions.RemoveEmptyEntries | StringSplitOptions.TrimEntries))
|
|
if (long.TryParse(part, out var parsedId) && parsedId > 0)
|
|
playerIds.Add(parsedId);
|
|
}
|
|
|
|
if (Request.ContentLength is > 0)
|
|
{
|
|
using var reader = new StreamReader(Request.Body, Encoding.UTF8, leaveOpen: true);
|
|
rawBody = await reader.ReadToEndAsync();
|
|
if (!string.IsNullOrWhiteSpace(rawBody))
|
|
{
|
|
var parsedBody = JsonNode.Parse(rawBody);
|
|
if (parsedBody is JsonArray array)
|
|
{
|
|
foreach (var item in array)
|
|
{
|
|
if (item is JsonValue value && value.TryGetValue<long>(out var bareId) && bareId > 0)
|
|
playerIds.Add(bareId);
|
|
else if (item is JsonObject obj)
|
|
playerIds.Add(ParsePlayerId(obj, new[] { "bannedPlayerId", "playerId", "accountId", "id" }));
|
|
}
|
|
}
|
|
else if (parsedBody is JsonObject body)
|
|
{
|
|
foreach (var key in new[] { "playerIds", "PlayerIds", "ids", "Ids", "bannedPlayerIds", "BannedPlayerIds", "players", "Players", "bans", "Bans" })
|
|
{
|
|
if (!body.TryGetPropertyValue(key, out var node) || node == null) continue;
|
|
if (node is JsonArray arr)
|
|
{
|
|
foreach (var item in arr)
|
|
{
|
|
if (item is JsonValue value && value.TryGetValue<long>(out var bareId) && bareId > 0)
|
|
playerIds.Add(bareId);
|
|
else if (item is JsonObject obj)
|
|
playerIds.Add(ParsePlayerId(obj, new[] { "bannedPlayerId", "playerId", "accountId", "id" }));
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
catch { }
|
|
|
|
playerIds = playerIds.Distinct().ToList();
|
|
|
|
if (playerIds.Count == 0)
|
|
return Success.Error("No valid player ids found in the import request");
|
|
|
|
var actorProfile = await db.Profiles.AsNoTracking().FirstOrDefaultAsync(x => x.accountId == (long)userId);
|
|
var imported = new List<RoomBan>();
|
|
var now = DateTime.UtcNow;
|
|
|
|
foreach (var targetPlayer in playerIds)
|
|
{
|
|
if (targetPlayer == (long)userId) continue;
|
|
if (await MatchmakingUtils.IsAtLeastRole(targetPlayer, (long)id!, RoleType.CoOwner)) continue;
|
|
if (await db.Profiles.AnyAsync(x => x.accountId == targetPlayer) == false) continue;
|
|
|
|
var existing = await db.RoomBans.FirstOrDefaultAsync(x => x.roomId == id && x.bannedPlayerId == targetPlayer &&
|
|
(!x.expiresAt.HasValue || x.expiresAt.Value > DateTime.UtcNow));
|
|
if (existing != null) continue;
|
|
|
|
var ban = new RoomBan
|
|
{
|
|
roomId = (long)id!,
|
|
actorId = (long)userId,
|
|
bannedPlayerId = targetPlayer,
|
|
bannedByName = actorProfile?.displayName ?? $"Player {(long)userId}",
|
|
reason = "",
|
|
bannedAt = now,
|
|
expiresAt = null
|
|
};
|
|
|
|
db.RoomBans.Add(ban);
|
|
imported.Add(ban);
|
|
}
|
|
|
|
await db.SaveChangesAsync();
|
|
|
|
return new Success(true, null, new Dictionary<string, object?>
|
|
{
|
|
["imported"] = imported.Count,
|
|
["importedCount"] = imported.Count,
|
|
["requested"] = playerIds.Count,
|
|
["bans"] = imported,
|
|
["playerIds"] = imported.Select(x => x.bannedPlayerId).ToArray()
|
|
});
|
|
}
|
|
|
|
[HttpPut("{id}/bans"), Authorize]
|
|
[HttpPost("{id}/bans"), Authorize]
|
|
public async Task<Success> AddRoomBan(long? id)
|
|
{
|
|
var userId = User.GetUserId();
|
|
if (userId == null || id == null) return new Success(false, "Invalid auth", null);
|
|
|
|
var room = await db.Rooms.FirstOrDefaultAsync(x => x.RoomId == id);
|
|
if (room == null) return Success.Error("Room not found");
|
|
|
|
var canModify = await MatchmakingUtils.CanModerateRoom((long)userId, (long)id!);
|
|
if (!canModify)
|
|
{
|
|
|
|
|
|
var deniedRole = await RoomUtils.GetRoleForPlayer((long)userId, (long)id!);
|
|
var highRanksVal = await db.ServerConfig.AsNoTracking().FirstOrDefaultAsync(x => x.Key == "HighRanks");
|
|
return Success.Error("Insufficient permissions");
|
|
}
|
|
|
|
long targetPlayer = 0;
|
|
string banReason = "";
|
|
|
|
|
|
|
|
|
|
|
|
string[] idKeys =
|
|
{
|
|
"bannedPlayerId", "bannedPlayerID", "bannedPlayer", "bannedplayerid",
|
|
"bannedId", "bannedid", "banned_id",
|
|
"bannerId", "bannerid", "banner_id",
|
|
"playerId", "playerid", "player_id", "player", "playerAccountId",
|
|
"bannedUserId", "bannedAccountId",
|
|
"accountId", "account_id",
|
|
"targetPlayerId", "targetPlayer", "targetId", "targetid", "target",
|
|
"victimId", "victimid",
|
|
"userId", "userid",
|
|
"id"
|
|
};
|
|
|
|
|
|
if (Request.HasFormContentType)
|
|
{
|
|
foreach (var key in idKeys)
|
|
if (targetPlayer <= 0 && long.TryParse(Request.Form[key], out var formPlayer)) targetPlayer = formPlayer;
|
|
if (string.IsNullOrEmpty(banReason))
|
|
foreach (var key in new[] { "reason", "banReason", "ban_reason" })
|
|
if (Request.Form.TryGetValue(key, out var formReason)) { banReason = formReason.ToString(); break; }
|
|
}
|
|
|
|
|
|
foreach (var key in idKeys)
|
|
if (targetPlayer <= 0 && long.TryParse(Request.Query[key], out var queryPlayer)) targetPlayer = queryPlayer;
|
|
if (string.IsNullOrEmpty(banReason))
|
|
foreach (var key in new[] { "reason", "banReason", "ban_reason" })
|
|
if (Request.Query.TryGetValue(key, out var queryReason)) { banReason = queryReason.ToString(); break; }
|
|
|
|
|
|
|
|
string rawBody = "";
|
|
try
|
|
{
|
|
using var reader = new StreamReader(Request.Body, Encoding.UTF8, leaveOpen: true);
|
|
rawBody = await reader.ReadToEndAsync();
|
|
if (!string.IsNullOrWhiteSpace(rawBody))
|
|
{
|
|
var parsedBody = JsonNode.Parse(rawBody);
|
|
if (parsedBody is JsonObject body)
|
|
{
|
|
if (targetPlayer <= 0) targetPlayer = ParsePlayerId(body, idKeys);
|
|
if (string.IsNullOrEmpty(banReason)) banReason = ParseJsonString(body, "reason", "banReason", "ban_reason");
|
|
}
|
|
else if (parsedBody is JsonValue bareValue && targetPlayer <= 0)
|
|
{
|
|
if (bareValue.TryGetValue<long>(out var bareId)) targetPlayer = bareId;
|
|
else if (bareValue.TryGetValue<string>(out var bareStr) && long.TryParse(bareStr, out var bareParsed)) targetPlayer = bareParsed;
|
|
}
|
|
}
|
|
}
|
|
catch { }
|
|
|
|
if (targetPlayer <= 0)
|
|
{
|
|
return Success.Error("bannedPlayerId is required");
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
if (targetPlayer == (long)userId)
|
|
{
|
|
return Success.Error("You cannot ban yourself");
|
|
}
|
|
|
|
|
|
|
|
if (await MatchmakingUtils.IsAtLeastRole(targetPlayer, (long)id!, RoleType.CoOwner))
|
|
return Success.Error("You cannot ban a Co-Owner or higher");
|
|
|
|
var profile = await db.Profiles.FirstOrDefaultAsync(x => x.accountId == targetPlayer);
|
|
if (profile == null) return Success.Error("Player not found");
|
|
|
|
var existing = await db.RoomBans.FirstOrDefaultAsync(x => x.roomId == id && x.bannedPlayerId == targetPlayer &&
|
|
(!x.expiresAt.HasValue || x.expiresAt.Value > DateTime.UtcNow));
|
|
if (existing != null) return Success.Error("Player is already banned from this room");
|
|
|
|
var actorProfile = await db.Profiles.AsNoTracking().FirstOrDefaultAsync(x => x.accountId == (long)userId);
|
|
var ban = new RoomBan
|
|
{
|
|
roomId = (long)id!,
|
|
actorId = (long)userId,
|
|
bannedPlayerId = targetPlayer,
|
|
bannedByName = actorProfile?.displayName ?? $"Player {(long)userId}",
|
|
reason = banReason.Trim(),
|
|
bannedAt = DateTime.UtcNow,
|
|
expiresAt = null
|
|
};
|
|
|
|
await db.RoomBans.AddAsync(ban);
|
|
await db.SaveChangesAsync();
|
|
|
|
|
|
await MatchmakingUtils.KickPlayerFromRoom(targetPlayer, (long)id!,
|
|
string.IsNullOrWhiteSpace(ban.reason) ? "You were banned from this room." : $"You were banned from this room: {ban.reason}");
|
|
|
|
return new Success(true, null, ban);
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
[HttpDelete("{id}/bans/{bannedPlayerId}"), HttpDelete("{id}/bans"),
|
|
HttpPost("{id}/bans/{bannedPlayerId}"), HttpPut("{id}/bans/{bannedPlayerId}"), Authorize]
|
|
public async Task<Success> RemoveRoomBan(long? id, long? bannedPlayerId)
|
|
{
|
|
var userId = User.GetUserId();
|
|
if (userId == null || id == null) return new Success(false, "Invalid auth", null);
|
|
|
|
var room = await db.Rooms.FirstOrDefaultAsync(x => x.RoomId == id);
|
|
if (room == null) return Success.Error("Room not found");
|
|
|
|
var canModify = await MatchmakingUtils.CanModerateRoom((long)userId, (long)id!);
|
|
if (!canModify) return Success.Error("Insufficient permissions");
|
|
|
|
|
|
|
|
|
|
long targetPlayer = bannedPlayerId ?? 0;
|
|
string[] idKeys =
|
|
{
|
|
"bannedPlayerId", "bannedPlayerID", "bannedId", "bannedid", "banned_id",
|
|
"playerId", "playerid", "player_id", "player",
|
|
"bannerId", "bannerid", "banner_id",
|
|
"userId", "userid", "id"
|
|
};
|
|
if (targetPlayer <= 0 && Request.HasFormContentType)
|
|
foreach (var key in idKeys)
|
|
if (long.TryParse(Request.Form[key], out var formPlayer)) { targetPlayer = formPlayer; break; }
|
|
if (targetPlayer <= 0)
|
|
foreach (var key in idKeys)
|
|
if (long.TryParse(Request.Query[key], out var queryPlayer)) { targetPlayer = queryPlayer; break; }
|
|
if (targetPlayer <= 0)
|
|
{
|
|
try
|
|
{
|
|
using var reader = new StreamReader(Request.Body, Encoding.UTF8, leaveOpen: true);
|
|
var raw = await reader.ReadToEndAsync();
|
|
if (!string.IsNullOrWhiteSpace(raw) && JsonNode.Parse(raw) is JsonObject body)
|
|
targetPlayer = ParsePlayerId(body, idKeys);
|
|
}
|
|
catch { }
|
|
}
|
|
|
|
if (targetPlayer <= 0)
|
|
{
|
|
return Success.Error("bannedPlayerId is required");
|
|
}
|
|
|
|
var ban = await db.RoomBans.FirstOrDefaultAsync(x => x.roomId == id && x.bannedPlayerId == targetPlayer);
|
|
if (ban == null) return Success.Error("Player is not banned from this room");
|
|
|
|
db.RoomBans.Remove(ban);
|
|
await db.SaveChangesAsync();
|
|
return new Success(true, null, ban);
|
|
}
|
|
|
|
[HttpPut("{id}/loadscreen")]
|
|
public async Task<Success> SetLoadingScreen(long id, string imageName, string title, string subtitle)
|
|
{
|
|
var userId = User.GetUserId();
|
|
if (userId == null) return new Success(false, "Invalid auth", null);
|
|
|
|
var canModify = await MatchmakingUtils.IsAtLeastRole((long)userId, (long)id!, RoleType.CoOwner);
|
|
if (!canModify) return Success.Error("Insufficient permissions");
|
|
|
|
var room = await db.Rooms.FirstOrDefaultAsync(x => x.RoomId == id);
|
|
if (room == null) return Success.Error("Room not found");
|
|
|
|
var entry = new RoomLoadScreen { ImageName = imageName, RoomId = room.RoomId, Subtitle = subtitle, Title = title };
|
|
List<RoomLoadScreen> loadScreen = room.loadScreen.FromJson<List<RoomLoadScreen>>() ?? new();
|
|
if (loadScreen.Count >= 1)
|
|
loadScreen[0] = entry;
|
|
else
|
|
loadScreen.Add(entry);
|
|
room.loadScreen = loadScreen.ToJson();
|
|
|
|
await db.SaveChangesAsync();
|
|
|
|
var fullRoom = await RoomUtils.GetRoom((long)id!);
|
|
await Notify.SendNotify((long)userId, new Notification { Id = "RoomUpdate", Msg = fullRoom });
|
|
|
|
return new Success(true, null, fullRoom);
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
private static long ParsePlayerId(JsonObject body, string[] idKeys)
|
|
{
|
|
foreach (var prop in body)
|
|
{
|
|
if (prop.Value is not JsonValue value) continue;
|
|
bool keyMatches = false;
|
|
foreach (var key in idKeys)
|
|
if (prop.Key.Equals(key, StringComparison.OrdinalIgnoreCase)) { keyMatches = true; break; }
|
|
if (!keyMatches) continue;
|
|
|
|
if (value.TryGetValue<long>(out var longId)) return longId;
|
|
if (value.TryGetValue<string>(out var strId) && long.TryParse(strId, out var parsed)) return parsed;
|
|
if (value.TryGetValue<double>(out var doubleId) && doubleId > 0 && doubleId == Math.Floor(doubleId)) return (long)doubleId;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
private static string ParseJsonString(JsonObject body, params string[] keys)
|
|
{
|
|
foreach (var prop in body)
|
|
{
|
|
if (prop.Value is not JsonValue value) continue;
|
|
bool keyMatches = false;
|
|
foreach (var key in keys)
|
|
if (prop.Key.Equals(key, StringComparison.OrdinalIgnoreCase)) { keyMatches = true; break; }
|
|
if (keyMatches && value.TryGetValue<string>(out var str)) return str ?? "";
|
|
}
|
|
return "";
|
|
}
|
|
}
|
|
}
|