mirror of
https://github.com/JogmansonReal/Radicade.git
synced 2026-10-26 15:39:01 -07:00
303 lines
8.3 KiB
C#
303 lines
8.3 KiB
C#
using Microsoft.AspNetCore.SignalR;
|
|
using Microsoft.EntityFrameworkCore;
|
|
using System.Text.Json;
|
|
using RadicadeServer.DB;
|
|
using RadicadeServer.Extensions;
|
|
using RadicadeServer.RecNet.Accounts;
|
|
using RadicadeServer.RecNet.Notifications;
|
|
using RadicadeServer.RecNet.Enums.Messages;
|
|
using RadicadeServer.Utils;
|
|
|
|
namespace RadicadeServer.API.Notifications
|
|
{
|
|
public class NotifyHub : Hub
|
|
{
|
|
private static readonly string[] TokenCarriers = { "access_token", "accessToken", "token", "id_token" };
|
|
|
|
private RecDBContext db;
|
|
public NotifyHub(IHubContext<NotifyHub> hubContext, RecDBContext db) {
|
|
Notify.Context = hubContext;
|
|
this.db = db;
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
private string? RawToken()
|
|
{
|
|
var httpContext = Context.GetHttpContext();
|
|
if (httpContext == null) return null;
|
|
|
|
foreach (var name in TokenCarriers)
|
|
{
|
|
var fromQuery = httpContext.Request.Query[name].ToString();
|
|
if (!string.IsNullOrWhiteSpace(fromQuery)) return fromQuery;
|
|
}
|
|
|
|
var header = httpContext.Request.Headers["Authorization"].ToString();
|
|
if (!string.IsNullOrWhiteSpace(header)) return header;
|
|
|
|
foreach (var name in TokenCarriers)
|
|
{
|
|
if (httpContext.Request.Cookies.TryGetValue(name, out var cookie) && !string.IsNullOrWhiteSpace(cookie))
|
|
return cookie;
|
|
}
|
|
|
|
return null;
|
|
}
|
|
|
|
private long? GetAccountId()
|
|
{
|
|
var authenticatedAccount = Context.User?.GetUserId();
|
|
if (authenticatedAccount != null) return authenticatedAccount;
|
|
|
|
var raw = RawToken();
|
|
return string.IsNullOrWhiteSpace(raw) ? null : raw.GetUserId();
|
|
}
|
|
|
|
private static JsonElement RawJson(string json) => JsonDocument.Parse(json).RootElement.Clone();
|
|
|
|
private async Task SendNotificationToCaller(string id, object msg)
|
|
{
|
|
var notification = new Notification { Id = id, Msg = msg };
|
|
await Clients.Caller.SendAsync("Notification", notification.ToJson());
|
|
}
|
|
|
|
private static List<long> ReadPlayerIds(object? update)
|
|
{
|
|
var ids = new List<long>();
|
|
|
|
void Add(JsonElement element)
|
|
{
|
|
if (element.ValueKind == JsonValueKind.Number && element.TryGetInt64(out var single))
|
|
{
|
|
ids.Add(single);
|
|
return;
|
|
}
|
|
|
|
if (element.ValueKind == JsonValueKind.String && long.TryParse(element.GetString(), out single))
|
|
{
|
|
ids.Add(single);
|
|
return;
|
|
}
|
|
|
|
if (element.ValueKind != JsonValueKind.Array) return;
|
|
|
|
foreach (var entry in element.EnumerateArray())
|
|
{
|
|
if (entry.ValueKind == JsonValueKind.Number && entry.TryGetInt64(out var value))
|
|
ids.Add(value);
|
|
else if (entry.ValueKind == JsonValueKind.String && long.TryParse(entry.GetString(), out value))
|
|
ids.Add(value);
|
|
}
|
|
}
|
|
|
|
try
|
|
{
|
|
if (update is SubscriptionUpdate subscription && subscription.PlayerIds != null)
|
|
{
|
|
ids.AddRange(subscription.PlayerIds);
|
|
return ids;
|
|
}
|
|
|
|
if (update is not JsonElement json) return ids;
|
|
|
|
if (json.ValueKind == JsonValueKind.Array)
|
|
{
|
|
Add(json);
|
|
return ids;
|
|
}
|
|
|
|
if (json.ValueKind != JsonValueKind.Object) return ids;
|
|
|
|
foreach (var property in json.EnumerateObject())
|
|
{
|
|
if (!property.Name.Equals("PlayerIds", StringComparison.OrdinalIgnoreCase)) continue;
|
|
Add(property.Value);
|
|
}
|
|
}
|
|
catch { }
|
|
|
|
return ids;
|
|
}
|
|
|
|
[HubMethodName("SubscribeToPlayers")]
|
|
public Task Subscribe(object? update) => ApplySubscriptions(update);
|
|
|
|
[HubMethodName("playerSubscriptions/v1/update")]
|
|
public Task SubscribeToPlayersV1(object? update) => ApplySubscriptions(update);
|
|
|
|
private async Task ApplySubscriptions(object? update)
|
|
{
|
|
long? account = GetAccountId();
|
|
if (account == null) return;
|
|
|
|
var ids = ReadPlayerIds(update);
|
|
|
|
var client = Notify.GetConnectionClient(Context.ConnectionId);
|
|
if (client != null)
|
|
{
|
|
lock (client.SubscriptionsLock)
|
|
{
|
|
client.Subscriptions = ids.Where(id => id > 0).Distinct().ToList();
|
|
client.HasSubscriptions = true;
|
|
}
|
|
await Notify.UpdateSubscriptionsForClient(Context.ConnectionId);
|
|
}
|
|
}
|
|
|
|
[HubMethodName("SubscribeTo")]
|
|
public Task SubscribeToChannels(object? update) => ApplyChannelSubscriptions(update, subscribing: true);
|
|
|
|
[HubMethodName("UnsubscribeFrom")]
|
|
public Task UnsubscribeFromChannels(object? update) => ApplyChannelSubscriptions(update, subscribing: false);
|
|
|
|
private Task ApplyChannelSubscriptions(object? update, bool subscribing)
|
|
{
|
|
var channels = ReadChannels(update);
|
|
if (channels.Count == 0) return Task.CompletedTask;
|
|
return Task.CompletedTask;
|
|
}
|
|
|
|
private static List<string> ReadChannels(object? update)
|
|
{
|
|
var channels = new List<string>();
|
|
|
|
try
|
|
{
|
|
if (update is not JsonElement json || json.ValueKind != JsonValueKind.Object) return channels;
|
|
|
|
foreach (var property in json.EnumerateObject())
|
|
{
|
|
if (!property.Name.Equals("Channels", StringComparison.OrdinalIgnoreCase)) continue;
|
|
if (property.Value.ValueKind != JsonValueKind.Array) continue;
|
|
|
|
foreach (var entry in property.Value.EnumerateArray())
|
|
{
|
|
var channel = entry.ValueKind == JsonValueKind.String ? entry.GetString() : null;
|
|
if (!string.IsNullOrWhiteSpace(channel)) channels.Add(channel!);
|
|
}
|
|
}
|
|
}
|
|
catch { }
|
|
|
|
return channels;
|
|
}
|
|
|
|
[HubMethodName("heartbeat2")]
|
|
public Task<object?> Heartbeat() => AnswerHeartbeat();
|
|
|
|
[HubMethodName("heartbeat")]
|
|
public Task<object?> HeartbeatUnversioned() => AnswerHeartbeat();
|
|
|
|
private async Task<object?> AnswerHeartbeat()
|
|
{
|
|
var account = GetAccountId();
|
|
if (account == null) return null;
|
|
|
|
var presence = await Notify.BuildSelfPresenceAsync(db, account.Value);
|
|
if (presence == null) return null;
|
|
|
|
await SendNotificationToCaller("PresenceUpdate", presence);
|
|
|
|
return RawJson($"{{\"Id\":{account.Value},\"Msg\":{presence.ToJson()}}}");
|
|
}
|
|
|
|
public override async Task OnConnectedAsync()
|
|
{
|
|
var httpContext = Context.GetHttpContext();
|
|
long? account = GetAccountId();
|
|
var tokenCarrier = string.IsNullOrWhiteSpace(RawToken()) ? "none" : "present";
|
|
|
|
|
|
if (account != null)
|
|
{
|
|
Notify.RegisterContext(Context);
|
|
|
|
lock (Notify.ClientsLock) Notify.Clients.Add(new HubClient
|
|
{
|
|
AccountId = account.Value,
|
|
ConnectionId = Context.ConnectionId
|
|
});
|
|
|
|
var activePresence = await db.Presences.FirstOrDefaultAsync(x => x.playerId == account.Value);
|
|
if (activePresence != null && !activePresence.isOnline && activePresence.roomInstance.HasValue)
|
|
{
|
|
activePresence.isOnline = true;
|
|
await db.SaveChangesAsync();
|
|
await Notify.UpdateSubscriptions(account.Value);
|
|
}
|
|
|
|
try { await Notify.SendSelfAccountUpdate(account.Value); }
|
|
catch (Exception) { }
|
|
|
|
try
|
|
{
|
|
var presence = await Notify.BuildSelfPresenceAsync(db, account.Value);
|
|
if (presence != null) await SendNotificationToCaller("PresenceUpdate", presence);
|
|
}
|
|
catch (Exception) { }
|
|
|
|
try
|
|
{
|
|
|
|
var ban = await db.ResolveBanAsync(account.Value);
|
|
if (ban != null)
|
|
{
|
|
var notification = new Notification { Id = NotificationType.ModerationKick, Msg = await PlayerUtils.BuildBanBlockDetailsAsync(db, ban, account.Value) };
|
|
await Clients.Caller.SendAsync("Notification", notification.ToJson());
|
|
}
|
|
} catch (Exception)
|
|
{
|
|
}
|
|
|
|
try
|
|
{
|
|
var pendingWarning = await db.GetPendingWarningAsync(account.Value);
|
|
if (pendingWarning != null)
|
|
{
|
|
await PlayerUtils.DeliverWarningAsync(account.Value,
|
|
PlayerUtils.BuildWarningBlockDetails(pendingWarning, (int)PlayerUtils.WarningScreenHold.TotalSeconds));
|
|
}
|
|
}
|
|
catch (Exception)
|
|
{
|
|
}
|
|
}
|
|
|
|
await base.OnConnectedAsync();
|
|
}
|
|
|
|
public override async Task OnDisconnectedAsync(Exception? exception)
|
|
{
|
|
var userId = GetAccountId();
|
|
|
|
lock (Notify.ClientsLock) Notify.Clients.RemoveAll(x => x.ConnectionId == Context.ConnectionId);
|
|
Notify.ForgetContext(Context.ConnectionId);
|
|
|
|
if (userId != null)
|
|
{
|
|
bool stillConnected;
|
|
lock (Notify.ClientsLock)
|
|
stillConnected = Notify.Clients.Any(x => x.AccountId == userId.Value);
|
|
|
|
if (!stillConnected)
|
|
{
|
|
var presence = await db.Presences.FirstOrDefaultAsync(x => x.playerId == userId.Value);
|
|
lock (Notify.ClientsLock)
|
|
stillConnected = Notify.Clients.Any(x => x.AccountId == userId.Value);
|
|
if (presence != null && !stillConnected)
|
|
{
|
|
presence.isOnline = false;
|
|
await db.SaveChangesAsync();
|
|
await Notify.UpdateSubscriptions(userId.Value);
|
|
}
|
|
}
|
|
}
|
|
|
|
await base.OnDisconnectedAsync(exception);
|
|
}
|
|
}
|
|
}
|