using DeluxeBackend.Services; using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Mvc; using System.ComponentModel.DataAnnotations; using System.Numerics; using DeluxeBackend.Models; using System.Security.Claims; using static DeluxeBackend.Enums; namespace DeluxeBackend.Controllers.Api { [Route("api/relationships")] [ApiController] public class RelationshipsController : ControllerBase//this is from KittyRec https://git.tabbycluster.net/KittyRec/KittyRec-Api/src/branch/main/Controllers/relationshipsController.cs { private readonly ILiteDbService db; private readonly IJwtService jwt; private readonly INotificationService ws; private readonly IMessageService messageService; public RelationshipsController(ILiteDbService _db, IJwtService _jwt, INotificationService _ws, IMessageService _messageService) { db = _db; jwt = _jwt; ws = _ws; messageService = _messageService; } private long? GetCurrentPlayerId() { string? raw = User.FindFirst(ClaimTypes.NameIdentifier)?.Value ?? User.FindFirst("sub")?.Value; if (long.TryParse(raw, out long id)) return id; return null; } private Relationship? GetRelationship(long playerId, long targetId) => db.Relationships .Include(x => x.Player) .Include(x => x.TargetPlayer) .FindOne(x => x.Player.Id == playerId && x.TargetPlayer.Id == targetId); private Relationship GetOrCreate(long playerId, long targetId) { var rel = GetRelationship(playerId, targetId); if (rel != null) return rel; rel = new Relationship { Player = db.Accounts.FindById(playerId), TargetPlayer = db.Accounts.FindById(targetId) }; db.Relationships.Insert(rel); return rel; } private async Task PushRelToPlayer(long toPlayerId, long aboutPlayerId) { var rel = GetRelationship(toPlayerId, aboutPlayerId); await ws.SendToPlayer(toPlayerId, PushNotification.RelationshipChanged, new Dictionary { ["PlayerID"] = aboutPlayerId, ["RelationshipType"] = (int)(rel?.RelationshipType ?? RelationshipType.None), ["Muted"] = (int)(rel?.Muted ?? MuteState.None), ["Ignored"] = (int)(rel?.Ignored ?? IgnoreState.None), ["Favorited"] = rel?.Favorited ?? 0 }); } private void DeleteFriendInvites(long fromPlayerId, long toPlayerId) { var messageToDelete = db.Messages.FindOne(m => m.FromPlayer.Id == fromPlayerId && m.Player.Id == toPlayerId && m.Type == MessageType.FriendInvite); if (messageToDelete != null) { db.Messages.Delete(messageToDelete.Id); ws.SendToPlayer(toPlayerId, PushNotification.MessageDeleted, new Dictionary { ["Id"] = messageToDelete.Id }); } } [HttpGet("v2/get")] [Authorize] public async Task get() { long? pid = GetCurrentPlayerId(); if (pid == null) return Unauthorized(); var rels = db.Relationships .Query() .Include(x => x.Player) .Include(x => x.TargetPlayer) .Where(x => x.Player.Id == pid.Value) .ToList() .Select(r => r.ToDictionary()) .ToList(); return Ok(rels); } [HttpGet("v2/sendfriendrequest")] [Authorize] public async Task SendFriendRequest([FromQuery] long id = 0) { long? pid = GetCurrentPlayerId(); if (pid == null) return Unauthorized(); if (id == 0 || id == pid || db.Accounts.FindById(id) == null) return Ok(); var myRel = GetOrCreate(pid.Value, id); var theirRel = GetOrCreate(id, pid.Value); if (myRel.RelationshipType == RelationshipType.Friend) return Ok(); // they already sent us one — auto-accept both sides if (theirRel.RelationshipType == RelationshipType.Sent || myRel.RelationshipType == RelationshipType.Received) { myRel.RelationshipType = RelationshipType.Friend; theirRel.RelationshipType = RelationshipType.Friend; db.Relationships.Update(myRel); db.Relationships.Update(theirRel); DeleteFriendInvites(pid.Value, id); Account me = db.Accounts.FindById(pid.Value); Account them = db.Accounts.FindById(id); await messageService.SendMessage(me, them, MessageType.FriendRequestAccepted, pid.Value.ToString()); await PushRelToPlayer(id, pid.Value); await PushRelToPlayer(pid.Value, id); return Ok(); } // new outgoing request myRel.RelationshipType = RelationshipType.Sent; theirRel.RelationshipType = RelationshipType.Received; db.Relationships.Update(myRel); db.Relationships.Update(theirRel); DeleteFriendInvites(id, pid.Value); Account sender = db.Accounts.FindById(pid.Value); Account recipient = db.Accounts.FindById(id); await messageService.SendMessage(sender, recipient, MessageType.FriendInvite, pid.Value.ToString()); await PushRelToPlayer(id, pid.Value); await PushRelToPlayer(pid.Value, id); return Ok(myRel.ToDictionary()); } [HttpGet("v2/addfriend")] [Authorize] public async Task AddFriend([FromQuery] long id = 0) => await SendFriendRequest(id); [HttpGet("v2/acceptfriendrequest")] [Authorize] public async Task AcceptFriendRequest([FromQuery] long id = 0) { long? pid = GetCurrentPlayerId(); if (pid == null) return Unauthorized(); if (id == 0 || id == pid) return Ok(); var myRel = GetOrCreate(pid.Value, id); var theirRel = GetOrCreate(id, pid.Value); if (theirRel.RelationshipType != RelationshipType.Sent && myRel.RelationshipType != RelationshipType.Received) return Ok(); myRel.RelationshipType = RelationshipType.Friend; theirRel.RelationshipType = RelationshipType.Friend; db.Relationships.Update(myRel); db.Relationships.Update(theirRel); DeleteFriendInvites(pid.Value, id); Account me = db.Accounts.FindById(pid.Value); Account them = db.Accounts.FindById(id); await messageService.SendMessage(me, them, MessageType.FriendRequestAccepted, pid.Value.ToString()); await PushRelToPlayer(id, pid.Value); await PushRelToPlayer(pid.Value, id); return Ok(myRel.ToDictionary()); } [HttpGet("v2/removefriend")] [Authorize] public async Task RemoveFriend([FromQuery] long id = 0) { long? pid = GetCurrentPlayerId(); if (pid == null) return Unauthorized(); if (id != 0 && id != pid) { var myRel = GetRelationship(pid.Value, id); var theirRel = GetRelationship(id, pid.Value); if (myRel != null) { myRel.RelationshipType = RelationshipType.None; db.Relationships.Update(myRel); } if (theirRel != null) { theirRel.RelationshipType = RelationshipType.None; db.Relationships.Update(theirRel); } DeleteFriendInvites(pid.Value, id); DeleteFriendInvites(id, pid.Value); await PushRelToPlayer(id, pid.Value); await PushRelToPlayer(pid.Value, id); Dictionary gg = new() { ["PlayerID"] = pid, ["RelationshipType"] = (int)(myRel?.RelationshipType ?? RelationshipType.None), ["Muted"] = (int)(myRel?.Muted ?? MuteState.None), ["Ignored"] = (int)(myRel?.Ignored ?? IgnoreState.None), ["Favorited"] = myRel?.Favorited ?? 0 }; return Ok(gg); } return Ok(); } [HttpGet("v1/favorite")] [Authorize] public async Task Favorite([FromQuery] long id = 0) { long? pid = GetCurrentPlayerId(); if (pid == null) return Unauthorized(); if (id != 0 && id != pid) { var rel = GetOrCreate(pid.Value, id); rel.Favorited = 1; db.Relationships.Update(rel); return Ok(rel.ToDictionary()); } return Ok(); } [HttpGet("v1/unfavorite")] [Authorize] public async Task Unfavorite([FromQuery] long id = 0) { long? pid = GetCurrentPlayerId(); if (pid == null) return Unauthorized(); if (id != 0 && id != pid) { var rel = GetOrCreate(pid.Value, id); rel.Favorited = 0; db.Relationships.Update(rel); return Ok(rel.ToDictionary()); } return Ok(); } [HttpPost("v1/mute")] [Authorize] public async Task Mute([FromForm(Name = "PlayerId"), Required] long playerId) { long? pid = GetCurrentPlayerId(); if (pid == null) return Unauthorized(); if (playerId == 0 || playerId == pid) return Ok(); var myRel = GetOrCreate(pid.Value, playerId); var theirRel = GetOrCreate(playerId, pid.Value); myRel.Muted = MuteState.Local; theirRel.Muted = MuteState.Remote; db.Relationships.Update(myRel); db.Relationships.Update(theirRel); await PushRelToPlayer(pid.Value, playerId); await PushRelToPlayer(playerId, pid.Value); return Ok(myRel.ToDictionary()); } [HttpPost("v1/unmute")] [Authorize] public async Task Unmute([FromForm(Name = "PlayerId"), Required] long playerId) { long? pid = GetCurrentPlayerId(); if (pid == null) return Unauthorized(); if (playerId == 0 || playerId == pid) return Ok(); var myRel = GetOrCreate(pid.Value, playerId); var theirRel = GetOrCreate(playerId, pid.Value); myRel.Muted = MuteState.None; if (theirRel.Muted == MuteState.Remote) theirRel.Muted = MuteState.None; db.Relationships.Update(myRel); db.Relationships.Update(theirRel); await PushRelToPlayer(pid.Value, playerId); await PushRelToPlayer(playerId, pid.Value); return Ok(myRel.ToDictionary()); } [HttpPost("v1/ignore")] [Authorize] public async Task Ignore([FromForm(Name = "PlayerId"), Required] long playerId) { long? pid = GetCurrentPlayerId(); if (pid == null) return Unauthorized(); if (playerId == 0 || playerId == pid) return Ok(); var myRel = GetOrCreate(pid.Value, playerId); var theirRel = GetOrCreate(playerId, pid.Value); myRel.Ignored = IgnoreState.Local; theirRel.Ignored = IgnoreState.Remote; db.Relationships.Update(myRel); db.Relationships.Update(theirRel); await PushRelToPlayer(pid.Value, playerId); await PushRelToPlayer(playerId, pid.Value); return Ok(myRel.ToDictionary()); } [HttpPost("v1/unignore")] [Authorize] public async Task Unignore([FromForm(Name = "PlayerId"), Required] long playerId) { long? pid = GetCurrentPlayerId(); if (pid == null) return Unauthorized(); if (playerId == 0 || playerId == pid) return Ok(); var myRel = GetOrCreate(pid.Value, playerId); var theirRel = GetOrCreate(playerId, pid.Value); myRel.Ignored = IgnoreState.None; if (theirRel.Ignored == IgnoreState.Remote) theirRel.Ignored = IgnoreState.None; db.Relationships.Update(myRel); db.Relationships.Update(theirRel); await PushRelToPlayer(pid.Value, playerId); await PushRelToPlayer(playerId, pid.Value); return Ok(myRel.ToDictionary()); } [HttpPost("v1/bulkignoreplatformusers")] public IActionResult BulkIgnorePlatformUsers() => Ok(); } }