diff --git a/Content.Server/Administration/Managers/BanManager.cs b/Content.Server/Administration/Managers/BanManager.cs index ad1a12ba7ed4..01cd0bfc2a05 100644 --- a/Content.Server/Administration/Managers/BanManager.cs +++ b/Content.Server/Administration/Managers/BanManager.cs @@ -6,6 +6,7 @@ using Content.Server.Chat.Managers; using Content.Server.Database; using Content.Server.GameTicking; +using Content.Server.SS220.Discord; using Content.Shared.CCVar; using Content.Shared.Database; using Content.Shared.Players; @@ -32,6 +33,7 @@ public sealed class BanManager : IBanManager, IPostInjectInit [Dependency] private readonly IChatManager _chat = default!; [Dependency] private readonly INetManager _netManager = default!; [Dependency] private readonly ILogManager _logManager = default!; + [Dependency] private readonly DiscordBanPostManager _discordBanPostManager = default!; private ISawmill _sawmill = default!; @@ -59,7 +61,7 @@ private async void OnPlayerStatusChanged(object? sender, SessionStatusEventArgs SendRoleBans(e.Session); } - private async Task AddRoleBan(ServerRoleBanDef banDef) + private async Task AddRoleBan(ServerRoleBanDef banDef) { banDef = await _db.AddServerRoleBanAsync(banDef); @@ -68,7 +70,7 @@ private async Task AddRoleBan(ServerRoleBanDef banDef) _cachedRoleBans.GetOrNew(banDef.UserId.Value).Add(banDef); } - return true; + return banDef; } public HashSet? GetRoleBans(NetUserId playerUserId) @@ -140,7 +142,7 @@ public async void CreateServerBan(NetUserId? target, string? targetUsername, Net statedRound, null); - await _db.AddServerBanAsync(banDef); + var banId = await _db.AddServerBanAsync(banDef); var adminName = banningAdmin == null ? Loc.GetString("system-user") : (await _db.GetPlayerRecordByUserId(banningAdmin.Value))?.LastSeenUserName ?? Loc.GetString("system-user"); @@ -168,6 +170,10 @@ public async void CreateServerBan(NetUserId? target, string? targetUsername, Net _sawmill.Info(logMessage); _chat.SendAdminAlert(logMessage); + // SS220 user ban info post start + await _discordBanPostManager.PostUserBanInfo(banId); + // SS220 user ban info post end + // If we're not banning a player we don't care about disconnecting people if (target == null) return; @@ -217,12 +223,21 @@ public async void CreateRoleBan(NetUserId? target, string? targetUsername, NetUs null, role); - if (!await AddRoleBan(banDef)) + banDef = await AddRoleBan(banDef); + + if (banDef is null) { _chat.SendAdminAlert(Loc.GetString("cmd-roleban-existing", ("target", targetUsername ?? "null"), ("role", role))); return; } + // SS220 user ban info post start + if (banDef.Id.HasValue) + { + await _discordBanPostManager.PostUserJobBanInfo(banDef.Id.Value, targetUsername); + } + // SS220 user ban info post end + var length = expires == null ? Loc.GetString("cmd-roleban-inf") : Loc.GetString("cmd-roleban-until", ("expires", expires)); _chat.SendAdminAlert(Loc.GetString("cmd-roleban-success", ("target", targetUsername ?? "null"), ("role", role), ("reason", reason), ("length", length))); diff --git a/Content.Server/Database/ServerDbBase.cs b/Content.Server/Database/ServerDbBase.cs index 4ef756c6d96e..690bf64da6f5 100644 --- a/Content.Server/Database/ServerDbBase.cs +++ b/Content.Server/Database/ServerDbBase.cs @@ -362,7 +362,7 @@ public abstract Task> GetServerBansAsync( ImmutableArray? hwId, bool includeUnbanned); - public abstract Task AddServerBanAsync(ServerBanDef serverBan); + public abstract Task AddServerBanAsync(ServerBanDef serverBan); public abstract Task AddServerUnbanAsync(ServerUnbanDef serverUnban); public async Task EditServerBan(int id, string reason, NoteSeverity severity, DateTimeOffset? expiration, Guid editedBy, DateTimeOffset editedAt) diff --git a/Content.Server/Database/ServerDbManager.cs b/Content.Server/Database/ServerDbManager.cs index 269c7bb0de24..e679fef0ebc3 100644 --- a/Content.Server/Database/ServerDbManager.cs +++ b/Content.Server/Database/ServerDbManager.cs @@ -85,7 +85,7 @@ Task> GetServerBansAsync( ImmutableArray? hwId, bool includeUnbanned=true); - Task AddServerBanAsync(ServerBanDef serverBan); + Task AddServerBanAsync(ServerBanDef serverBan); Task AddServerUnbanAsync(ServerUnbanDef serverBan); public Task EditServerBan( @@ -418,7 +418,7 @@ public Task> GetServerBansAsync( return RunDbCommand(() => _db.GetServerBansAsync(address, userId, hwId, includeUnbanned)); } - public Task AddServerBanAsync(ServerBanDef serverBan) + public Task AddServerBanAsync(ServerBanDef serverBan) { DbWriteOpsMetric.Inc(); return RunDbCommand(() => _db.AddServerBanAsync(serverBan)); diff --git a/Content.Server/Database/ServerDbPostgres.cs b/Content.Server/Database/ServerDbPostgres.cs index ba476b2cd20e..1404d7652b9a 100644 --- a/Content.Server/Database/ServerDbPostgres.cs +++ b/Content.Server/Database/ServerDbPostgres.cs @@ -230,11 +230,11 @@ private static IQueryable MakeBanLookupQuery( unban.UnbanTime); } - public override async Task AddServerBanAsync(ServerBanDef serverBan) + public override async Task AddServerBanAsync(ServerBanDef serverBan) { await using var db = await GetDbImpl(); - db.PgDbContext.Ban.Add(new ServerBan + var ban = db.PgDbContext.Ban.Add(new ServerBan { Address = serverBan.Address.ToNpgsqlInet(), HWId = serverBan.HWId?.ToArray(), @@ -251,6 +251,8 @@ public override async Task AddServerBanAsync(ServerBanDef serverBan) }); await db.PgDbContext.SaveChangesAsync(); + + return ban.Entity.Id; } public override async Task AddServerUnbanAsync(ServerUnbanDef serverUnban) diff --git a/Content.Server/Database/ServerDbSqlite.cs b/Content.Server/Database/ServerDbSqlite.cs index 74ac45fd88b9..67f4aac2b9e3 100644 --- a/Content.Server/Database/ServerDbSqlite.cs +++ b/Content.Server/Database/ServerDbSqlite.cs @@ -153,11 +153,11 @@ private static bool BanMatches(ServerBan ban, return hwId is { Length: > 0 } hwIdVar && hwIdVar.AsSpan().SequenceEqual(ban.HWId); } - public override async Task AddServerBanAsync(ServerBanDef serverBan) + public override async Task AddServerBanAsync(ServerBanDef serverBan) { await using var db = await GetDbImpl(); - db.SqliteDbContext.Ban.Add(new ServerBan + var ban = db.SqliteDbContext.Ban.Add(new ServerBan { Address = serverBan.Address.ToNpgsqlInet(), Reason = serverBan.Reason, @@ -174,6 +174,8 @@ public override async Task AddServerBanAsync(ServerBanDef serverBan) }); await db.SqliteDbContext.SaveChangesAsync(); + + return ban.Entity.Id; } public override async Task AddServerUnbanAsync(ServerUnbanDef serverUnban) diff --git a/Content.Server/Entry/EntryPoint.cs b/Content.Server/Entry/EntryPoint.cs index b8f69252f60d..dcd0d2e228da 100644 --- a/Content.Server/Entry/EntryPoint.cs +++ b/Content.Server/Entry/EntryPoint.cs @@ -115,6 +115,7 @@ public override void Init() IoCManager.Resolve().Initialize(); IoCManager.Resolve().Initialize(); IoCManager.Resolve().Initialize(); // SS220 discord player manager + IoCManager.Resolve().Initialize(); // SS220 discord ban post manager IoCManager.Resolve().Initialize(); _voteManager.Initialize(); diff --git a/Content.Server/IoC/ServerContentIoC.cs b/Content.Server/IoC/ServerContentIoC.cs index e3f7953be8f4..a98992b856b4 100644 --- a/Content.Server/IoC/ServerContentIoC.cs +++ b/Content.Server/IoC/ServerContentIoC.cs @@ -68,7 +68,8 @@ public static void Register() IoCManager.Register(); IoCManager.Register(); IoCManager.Register(); - IoCManager.Register(); + IoCManager.Register(); // SS220 discord player manager + IoCManager.Register(); // SS220 discord ban post manager IoCManager.Register(); IoCManager.Register(); IoCManager.Register(); diff --git a/Content.Server/SS220/Discord/DiscordBanPostManager.cs b/Content.Server/SS220/Discord/DiscordBanPostManager.cs new file mode 100644 index 000000000000..eebb60d1e411 --- /dev/null +++ b/Content.Server/SS220/Discord/DiscordBanPostManager.cs @@ -0,0 +1,155 @@ +// © SS220, An EULA/CLA with a hosting restriction, full text: https://raw.githubusercontent.com/SerbiaStrong-220/space-station-14/master/CLA.txt + +using Content.Shared.Corvax.CCCVars; +using Robust.Shared.Configuration; +using System.Net; +using System.Net.Http; +using System.Net.Http.Headers; +using System.Text; +using System.Timers; +using System.Threading.Tasks; +using System.Text.Json; + +namespace Content.Server.SS220.Discord; + +public sealed class DiscordBanPostManager +{ + [Dependency] private readonly IConfigurationManager _cfg = default!; + + private ISawmill _sawmill = default!; + + private readonly HttpClient _httpClient = new(); + private string _apiUrl = string.Empty; + + public void Initialize() + { + _sawmill = Logger.GetSawmill("DiscordPlayerManager"); + + _cfg.OnValueChanged(CCCVars.DiscordAuthApiUrl, v => _apiUrl = v, true); + _cfg.OnValueChanged(CCCVars.DiscordAuthApiKey, v => + { + _httpClient.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", v); + }, + true); + } + + public async Task PostUserBanInfo(int banId) + { + if (string.IsNullOrEmpty(_apiUrl)) + { + return; + } + + try + { + var url = $"{_apiUrl}/userBan/{banId}"; + + var response = await _httpClient.PostAsync(url, content: null); + + if (response.StatusCode != HttpStatusCode.OK) + { + var errorText = await response.Content.ReadAsStringAsync(); + + _sawmill.Error( + "Failed to post user ban: [{StatusCode}] {Response}", + response.StatusCode, + errorText); + } + } + catch (Exception exc) + { + _sawmill.Error($"Error while posting user ban. {exc.Message}"); + } + } + + private readonly Dictionary> _userBanCache = new(); + + private readonly Dictionary _userJobBanPostTimers = new(); + + public async Task PostUserJobBanInfo(int banId, string? targetUserName) + { + try + { + if (!string.IsNullOrWhiteSpace(targetUserName)) + { + AddUserJobBanToCache(banId, targetUserName); + AddUserJobBanTimer(targetUserName); + } + } + catch (Exception exc) + { + _sawmill.Error($"Error while cached user role ban. {exc.Message}"); + } + } + + private void AddUserJobBanTimer(string targetUserName) + { + if (!_userJobBanPostTimers.TryGetValue(targetUserName, out var timer)) + { + timer = new() + { + AutoReset = false + }; + + timer.Elapsed += async (sender, e) => await JobBanProccessComplete(targetUserName); + + _userJobBanPostTimers[targetUserName] = timer; + } + + timer.Stop(); + + timer.Interval = TimeSpan.FromMinutes(1).TotalMilliseconds; + timer.Start(); + } + + private async Task JobBanProccessComplete(string userName) + { + if (string.IsNullOrEmpty(_apiUrl)) + { + return; + } + + _userBanCache.Remove(userName, out var bans); + + if (bans is null) + { + return; + } + + try + { + var url = $"{_apiUrl}/userBan/roleBan"; + + var response = await _httpClient.PostAsync(url, + new StringContent( + JsonSerializer.Serialize(bans), + Encoding.UTF8, + "application/json")); + + if (response.StatusCode != HttpStatusCode.OK) + { + var errorText = await response.Content.ReadAsStringAsync(); + + _sawmill.Error( + "Failed to post user role ban: [{StatusCode}] {Response}", + response.StatusCode, + errorText); + } + } + catch (Exception exc) + { + _sawmill.Error($"Error while posting user role ban. {exc.Message}"); + } + } + + private void AddUserJobBanToCache(int banId, string targetUsername) + { + if (!_userBanCache.TryGetValue(targetUsername, out var cache)) + { + cache = []; + _userBanCache[targetUsername] = cache; + } + + cache.Add(banId); + } +}