using Mocha2023.Auth; using Mocha2023.Classes.DBs; using Microsoft.AspNetCore.Mvc; using System.Text.Json; using static Mocha2023.Classes.DBs.DBClasses.PlayerDBClasses; namespace Mocha2023.Controllers { [ApiController] [Mocha2023.Classes.ApiProtection] public class PartyController : ControllerBase { [HttpGet("/api/party/v1/current")] [HttpGet("/api/parties/v1/current")] [HttpGet("/match/party/current")] public IActionResult GetCurrentParty() { long? playerId = AuthStuff.GetPlayerId(Request); if (!playerId.HasValue) return Unauthorized(); PartySnapshot? party = PlayerDB.GetPartySnapshot(playerId.Value); return Ok(ToResponse(party)); } [HttpGet("/settings/partyinvite")] public IActionResult GetPartyInviteSetting() { var player = AuthStuff.GetCurrentPlayer(Request); if (player?.Player == null) return Unauthorized(); bool enabled = true; var setting = player.Player.PlayerExtra?.Settings?.FirstOrDefault(value => string.Equals(value.Key, "PartyInvite", StringComparison.OrdinalIgnoreCase) || string.Equals(value.Key, "AllowPartyInvites", StringComparison.OrdinalIgnoreCase) || string.Equals(value.Key, "Settings.PartyInvite", StringComparison.OrdinalIgnoreCase)); if (setting != null && bool.TryParse(setting.Value, out bool parsed)) enabled = parsed; Console.WriteLine( $"[PARTY INVITE SETTING] player={player.PlayerId} enabled={enabled}"); return Ok(enabled); } [HttpGet("/api/party/v1/invites")] [HttpGet("/api/parties/v1/invites")] public IActionResult GetPartyInvites() { long? playerId = AuthStuff.GetPlayerId(Request); if (!playerId.HasValue) return Unauthorized(); var invites = PlayerDB.GetPendingPartyInvites(playerId.Value) .Select(invite => new { partyId = invite.PartyId, inviterPlayerId = invite.InviterPlayerId, createdAt = invite.CreatedAt }) .ToArray(); return Ok(invites); } [HttpPost("/api/party/v1/invite")] [HttpPost("/api/parties/v1/invite")] [HttpPost("/match/party/invite")] public async Task InviteToParty() { long? inviterPlayerId = AuthStuff.GetPlayerId(Request); if (!inviterPlayerId.HasValue) return Unauthorized(); long? targetPlayerId = await ReadPlayerIdAsync( "targetPlayerId", "targetAccountId", "inviteePlayerId", "inviteeAccountId", "playerId", "accountId", "id"); if (!targetPlayerId.HasValue) return BadRequest(new { success = false, error = "missing_target_player" }); PartySnapshot? party = PlayerDB.InviteToParty( inviterPlayerId.Value, targetPlayerId.Value); if (party == null) return BadRequest(new { success = false, error = "party_invite_failed" }); await NotiController.NotifyPartyInviteAsync( inviterPlayerId.Value, targetPlayerId.Value, party.PartyId); return Ok(new { success = true, invitedPlayerId = targetPlayerId.Value, party = ToResponse(party) }); } [HttpPost("/api/party/v1/accept")] [HttpPost("/api/party/v1/join")] [HttpPost("/api/parties/v1/accept")] [HttpPost("/match/party/accept")] [HttpPost("/match/party/join")] public async Task AcceptPartyInvite() { long? inviteePlayerId = AuthStuff.GetPlayerId(Request); if (!inviteePlayerId.HasValue) return Unauthorized(); long? inviterPlayerId = await ReadPlayerIdAsync( "inviterPlayerId", "inviterAccountId", "leaderPlayerId", "leaderAccountId", "playerId", "accountId", "id"); if (!inviterPlayerId.HasValue) return BadRequest(new { success = false, error = "missing_inviter_player" }); PartySnapshot? party = PlayerDB.AcceptPartyInvite( inviteePlayerId.Value, inviterPlayerId.Value); return party == null ? BadRequest(new { success = false, error = "party_join_failed" }) : Ok(new { success = true, party = ToResponse(party) }); } [HttpPost("/api/party/v1/decline")] [HttpPost("/api/parties/v1/decline")] [HttpPost("/match/party/decline")] public async Task DeclinePartyInvite() { long? inviteePlayerId = AuthStuff.GetPlayerId(Request); if (!inviteePlayerId.HasValue) return Unauthorized(); long? inviterPlayerId = await ReadPlayerIdAsync( "inviterPlayerId", "inviterAccountId", "playerId", "accountId", "id"); if (!inviterPlayerId.HasValue) return BadRequest(new { success = false, error = "missing_inviter_player" }); return PlayerDB.DeclinePartyInvite( inviteePlayerId.Value, inviterPlayerId.Value) ? Ok(new { success = true }) : NotFound(new { success = false, error = "party_invite_not_found" }); } [HttpPost("/api/party/v1/leave")] [HttpPost("/api/parties/v1/leave")] [HttpPost("/match/party/leave")] public IActionResult LeaveParty() { long? playerId = AuthStuff.GetPlayerId(Request); if (!playerId.HasValue) return Unauthorized(); PartySnapshot? party = PlayerDB.LeaveParty(playerId.Value); return Ok(new { success = true, party = ToResponse(party) }); } private async Task ReadPlayerIdAsync(params string[] keys) { foreach (string key in keys) { if (long.TryParse(Request.Query[key].FirstOrDefault(), out long queryId)) return queryId; } if (Request.HasFormContentType) { var form = await Request.ReadFormAsync(); foreach (string key in keys) { if (long.TryParse(form[key].FirstOrDefault(), out long formId)) return formId; } } else if ((Request.ContentLength ?? 0) > 0) { using var reader = new StreamReader(Request.Body); string body = await reader.ReadToEndAsync(); if (long.TryParse(body.Trim().Trim('"'), out long rawId)) return rawId; try { using JsonDocument document = JsonDocument.Parse(body); long? id = FindPlayerId(document.RootElement, keys); if (id.HasValue) return id; } catch (JsonException) { foreach (string pair in body.Split('&', StringSplitOptions.RemoveEmptyEntries)) { string[] parts = pair.Split('=', 2); if (parts.Length != 2 || !keys.Contains(parts[0], StringComparer.OrdinalIgnoreCase)) { continue; } if (long.TryParse(Uri.UnescapeDataString(parts[1]), out long bodyId)) return bodyId; } } } return null; } private static long? FindPlayerId(JsonElement element, IReadOnlyCollection keys) { switch (element.ValueKind) { case JsonValueKind.Number: return element.TryGetInt64(out long number) ? number : null; case JsonValueKind.String: return long.TryParse(element.GetString(), out long textId) ? textId : null; case JsonValueKind.Object: foreach (JsonProperty property in element.EnumerateObject()) { if (keys.Contains(property.Name, StringComparer.OrdinalIgnoreCase)) { long? direct = FindPlayerId(property.Value, keys); if (direct.HasValue) return direct; } } foreach (JsonProperty property in element.EnumerateObject()) { long? nested = FindPlayerId(property.Value, keys); if (nested.HasValue) return nested; } break; case JsonValueKind.Array: foreach (JsonElement item in element.EnumerateArray()) { long? nested = FindPlayerId(item, keys); if (nested.HasValue) return nested; } break; } return null; } private static object ToResponse(PartySnapshot? party) { if (party == null) { return new { partyId = 0L, leaderPlayerId = 0L, memberPlayerIds = Array.Empty() }; } return new { partyId = party.PartyId, leaderPlayerId = party.LeaderPlayerId, memberPlayerIds = party.MemberPlayerIds.ToArray() }; } } }