From e6a09f22844444f2cda73f0fdeea5061ba9bee8c Mon Sep 17 00:00:00 2001 From: Sh1ntra Date: Thu, 14 Nov 2024 14:25:23 +0300 Subject: [PATCH 1/3] ds ahelp frontier --- Content.Server/Administration/ServerApi.cs | 44 ++++- .../Administration/Systems/BwoinkSystem.cs | 153 +++++++++++++++++- 2 files changed, 195 insertions(+), 2 deletions(-) diff --git a/Content.Server/Administration/ServerApi.cs b/Content.Server/Administration/ServerApi.cs index f964e8961ce..82712d81cae 100644 --- a/Content.Server/Administration/ServerApi.cs +++ b/Content.Server/Administration/ServerApi.cs @@ -6,12 +6,15 @@ using System.Text.Json; using System.Text.Json.Nodes; using System.Threading.Tasks; +using Content.Server.Administration.Managers; using Content.Server.Administration.Systems; +using Content.Server.Database; using Content.Server.GameTicking; using Content.Server.GameTicking.Presets; using Content.Server.GameTicking.Rules.Components; using Content.Server.Maps; using Content.Server.RoundEnd; +using Content.Shared.Administration; using Content.Shared.Administration.Managers; using Content.Shared.CCVar; using Content.Shared.GameTicking.Components; @@ -19,6 +22,7 @@ using Robust.Server.ServerStatus; using Robust.Shared.Asynchronous; using Robust.Shared.Configuration; +using Robust.Shared.Console; using Robust.Shared.Network; using Robust.Shared.Player; using Robust.Shared.Prototypes; @@ -58,6 +62,10 @@ public sealed partial class ServerApi : IPostInjectInit [Dependency] private readonly ILogManager _logManager = default!; [Dependency] private readonly IEntitySystemManager _entitySystemManager = default!; [Dependency] private readonly ILocalizationManager _loc = default!; + [Dependency] private readonly IPlayerLocator _locator = default!; + [Dependency] private readonly IConsoleHost _shell = default!; + [Dependency] private readonly IServerDbManager _db = default!; + [Dependency] private readonly IBanManager _bans = default!; private string _token = string.Empty; private ISawmill _sawmill = default!; @@ -395,6 +403,35 @@ await RunOnMainThread(async () => }); } + private async Task ActionAhelpSend(IStatusHandlerContext context, Actor actor) + { + var body = await ReadJson(context); + if (body == null) + return; + if (body.Text == null) + { + await context.RespondErrorAsync(HttpStatusCode.BadRequest); + return; + } + var bwoinkSystem = _entitySystemManager.GetEntitySystem(); + var data = await _locator.LookupIdByNameOrIdAsync($"{body.PlayerNickname}"); + if (data != null) + { + var playerUserId = new NetUserId(data.UserId); + + var senderUserId = new NetUserId(body.SenderUserId); + var message = new SharedBwoinkSystem.BwoinkTextMessage(playerUserId, senderUserId, body.Text); + await RunOnMainThread(async () => + { + if (_playerManager.TryGetSessionById(playerUserId, out var session)) + { + bwoinkSystem.DiscordAhelpSendMessage(message, new EntitySessionEventArgs(session)); + await RespondOk(context); + } + }); + } + } + #endregion #region Fetching @@ -603,7 +640,12 @@ await RespondError( } #region From Client - + private sealed class DiscordAhelpBody + { + public required string PlayerNickname { get; init; } + public required Guid SenderUserId { get; init; } + public string? Text { get; init; } + } private sealed class Actor { public required Guid Guid { get; init; } diff --git a/Content.Server/Administration/Systems/BwoinkSystem.cs b/Content.Server/Administration/Systems/BwoinkSystem.cs index 893de4aba5b..258cb2d8901 100644 --- a/Content.Server/Administration/Systems/BwoinkSystem.cs +++ b/Content.Server/Administration/Systems/BwoinkSystem.cs @@ -42,6 +42,8 @@ public sealed partial class BwoinkSystem : SharedBwoinkSystem [Dependency] private readonly IAfkManager _afkManager = default!; [Dependency] private readonly IServerDbManager _dbManager = default!; [Dependency] private readonly PlayerRateLimitManager _rateLimit = default!; + [Dependency] private readonly IPlayerLocator _locator = default!; + [Dependency] private readonly IServerDbManager _db = default!; [GeneratedRegex(@"^https://discord\.com/api/webhooks/(\d+)/((?!.*/).*)$")] private static partial Regex DiscordRegex(); @@ -727,8 +729,157 @@ private static string GenerateAHelpMessage(AHelpMessageParams parameters) stringbuilder.Append(parameters.Message); return stringbuilder.ToString(); } - } + public async void DiscordAhelpSendMessage(BwoinkTextMessage message, EntitySessionEventArgs eventArgs) + { + base.OnBwoinkTextMessage(message, eventArgs); + _activeConversations[message.UserId] = DateTime.Now; + var senderSession = eventArgs.SenderSession; + var data = await _locator.LookupIdByNameOrIdAsync($"{message.TrueSender}"); + if (data != null) + { + var adminNickname = data.Username; + // TODO: Sanitize text? + // Confirm that this person is actually allowed to send a message here. + var personalChannel = senderSession.UserId == message.UserId; + var senderAdmin = _adminManager.GetAdminData(senderSession); + var senderAHelpAdmin = senderAdmin?.HasFlag(AdminFlags.Adminhelp) ?? false; + var authorized = personalChannel || senderAHelpAdmin; + if (!authorized) + { + // Unauthorized bwoink (log?) + return; + } + + if (_rateLimit.CountAction(eventArgs.SenderSession, RateLimitKey) != RateLimitStatus.Allowed) + return; + + var escapedText = FormattedMessage.EscapeText(message.Text); + + string bwoinkText; + string adminPrefix = ""; + + //Getting an administrator position + if (_config.GetCVar(CCVars.AhelpAdminPrefix) && senderAdmin is not null && + senderAdmin.Title is not null) + { + adminPrefix = $"[bold]\\[{senderAdmin.Title}\\][/bold] "; + } + + if (senderAdmin is not null && + senderAdmin.Flags == + AdminFlags.Adminhelp) // Mentor. Not full admin. That's why it's colored differently. + { + bwoinkText = $"[color=purple]{adminPrefix}{adminNickname}[/color]"; + } + else if (senderAdmin is not null && senderAdmin.HasFlag(AdminFlags.Adminhelp)) + { + bwoinkText = $"[color=red]{adminPrefix}{adminNickname}[/color]"; + } + else + { + bwoinkText = $"[color=blue]{adminNickname}[/color]"; + } + + bwoinkText = $"{(message.PlaySound ? "" : "(S) ")}(Discord){bwoinkText}: {escapedText}"; + + // If it's not an admin / admin chooses to keep the sound then play it. + var playSound = !senderAHelpAdmin || message.PlaySound; + var msg = new BwoinkTextMessage(message.UserId, senderSession.UserId, bwoinkText, playSound: playSound); + + LogBwoink(msg); + + var admins = GetTargetAdmins(); + + // Notify all admins + foreach (var channel in admins) + { + RaiseNetworkEvent(msg, channel); + } + + string adminPrefixWebhook = ""; + + if (_config.GetCVar(CCVars.AhelpAdminPrefixWebhook) && senderAdmin is not null && + senderAdmin.Title is not null) + { + adminPrefixWebhook = $"[bold]\\[{senderAdmin.Title}\\][/bold] "; + } + + // Notify player + if (_playerManager.TryGetSessionById(message.UserId, out var session)) + { + if (!admins.Contains(session.Channel)) + { + // If _overrideClientName is set, we generate a new message with the override name. The admins name will still be the original name for the webhooks. + if (_overrideClientName != string.Empty) + { + string overrideMsgText; + // Doing the same thing as above, but with the override name. Theres probably a better way to do this. + if (senderAdmin is not null && + senderAdmin.Flags == + AdminFlags.Adminhelp) // Mentor. Not full admin. That's why it's colored differently. + { + overrideMsgText = $"[color=purple]{adminPrefixWebhook}{_overrideClientName}[/color]"; + } + else if (senderAdmin is not null && senderAdmin.HasFlag(AdminFlags.Adminhelp)) + { + overrideMsgText = $"[color=red]{adminPrefixWebhook}{_overrideClientName}[/color]"; + } + else + { + overrideMsgText = $"[color=blue]{senderSession.Name}[/color]"; // Not an admin, name is not overridden. + } + + overrideMsgText = $"{(message.PlaySound ? "" : "(S) ")}(Discord){overrideMsgText}: {escapedText}"; + + RaiseNetworkEvent(new BwoinkTextMessage(message.UserId, + senderSession.UserId, + overrideMsgText, + playSound: playSound), + session.Channel); + } + else + RaiseNetworkEvent(msg, session.Channel); + } + } + + var sendsWebhook = _webhookUrl != string.Empty; + if (sendsWebhook) + { + if (!_messageQueues.ContainsKey(msg.UserId)) + _messageQueues[msg.UserId] = new Queue(); + + var str = message.Text; + var unameLength = senderSession.Name.Length; + + if (unameLength + str.Length + _maxAdditionalChars > DescriptionMax) + { + str = str[..(DescriptionMax - _maxAdditionalChars - unameLength)]; + } + + var nonAfkAdmins = GetNonAfkAdmins(); + var messageParams = new AHelpMessageParams( + senderSession.Name, + str, + !personalChannel, + _gameTicker.RoundDuration().ToString("hh\\:mm\\:ss"), + _gameTicker.RunLevel, + playedSound: playSound, + noReceivers: nonAfkAdmins.Count == 0 + ); + _messageQueues[msg.UserId].Enqueue(GenerateAHelpMessage(messageParams)); + } + + if (admins.Count != 0 || sendsWebhook) + return; + + // No admin online, let the player know + var systemText = Loc.GetString("bwoink-system-starmute-message-no-other-users"); + var starMuteMsg = new BwoinkTextMessage(message.UserId, SystemUserId, systemText); + RaiseNetworkEvent(starMuteMsg, senderSession.Channel); + } + } + } public sealed class AHelpMessageParams { public string Username { get; set; } From 376568772e5294e1a2f771fa8781e9edf7e8337d Mon Sep 17 00:00:00 2001 From: Sh1ntra Date: Thu, 14 Nov 2024 14:56:16 +0300 Subject: [PATCH 2/3] ds ahelp 2 --- Content.Server/Administration/ServerApi.cs | 1 + 1 file changed, 1 insertion(+) diff --git a/Content.Server/Administration/ServerApi.cs b/Content.Server/Administration/ServerApi.cs index 82712d81cae..2efdca9034e 100644 --- a/Content.Server/Administration/ServerApi.cs +++ b/Content.Server/Administration/ServerApi.cs @@ -81,6 +81,7 @@ void IPostInjectInit.PostInject() // Post RegisterActorHandler(HttpMethod.Post, "/admin/actions/round/start", ActionRoundStart); + RegisterActorHandler(HttpMethod.Post, "/admin/actions/ahelp/send", ActionAhelpSend); RegisterActorHandler(HttpMethod.Post, "/admin/actions/round/end", ActionRoundEnd); RegisterActorHandler(HttpMethod.Post, "/admin/actions/round/restartnow", ActionRoundRestartNow); RegisterActorHandler(HttpMethod.Post, "/admin/actions/kick", ActionKick); From 91ccadc6422ff593cf0ca782f4eae3af2b0a4767 Mon Sep 17 00:00:00 2001 From: FireFoxPhoenix Date: Fri, 15 Nov 2024 22:31:51 +1000 Subject: [PATCH 3/3] =?UTF-8?q?=D0=A3=D0=B4=D0=B0=D0=BB=D0=B5=D0=BD=D0=B8?= =?UTF-8?q?=D0=B5=20=D1=81=D0=BA=D0=B0=D1=82=D0=B0=20=D0=B8=D0=B7=20=D0=BF?= =?UTF-8?q?=D1=83=D0=BB=D0=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Corvax/Shipyard/Nfsd/bigskat.yml | 64 +++++++++---------- 1 file changed, 32 insertions(+), 32 deletions(-) diff --git a/Resources/Prototypes/Corvax/Shipyard/Nfsd/bigskat.yml b/Resources/Prototypes/Corvax/Shipyard/Nfsd/bigskat.yml index a03583fa100..95cc38f3488 100644 --- a/Resources/Prototypes/Corvax/Shipyard/Nfsd/bigskat.yml +++ b/Resources/Prototypes/Corvax/Shipyard/Nfsd/bigskat.yml @@ -1,34 +1,34 @@ -- type: vessel - id: BigSkat - name: NSF Большой скат - description: Сверхогромный многофункциональный шаттл ДСБФ. Рекомендованный состав команды 10-16 человек. - price: 535500 #Appraisal value is 440000 - category: Large - group: Security - access: Frontier - shuttlePath: /Maps/Corvax/Shuttles/Nfsd/bigskat.yml +#- type: vessel +# id: BigSkat +# name: NSF Большой скат +# description: Сверхогромный многофункциональный шаттл ДСБФ. Рекомендованный состав команды 10-16 человек. +# price: 535500 #Appraisal value is 440000 +# category: Large +# group: Security +# access: Frontier +# shuttlePath: /Maps/Corvax/Shuttles/Nfsd/bigskat.yml -- type: gameMap - id: BigSkat - mapName: 'NSF Большой скат' - mapPath: /Maps/Corvax/Shuttles/Nfsd/bigskat.yml - minPlayers: 0 - stations: - BigSkat: - stationProto: StandardFrontierSecurityVessel - components: - - type: StationNameSetup - mapNameTemplate: 'Большой скат {1}' - nameGenerator: - !type:NanotrasenNameGenerator - prefixCreator: '14' - - type: StationJobs - availableJobs: +#- type: gameMap +# id: BigSkat +# mapName: 'NSF Большой скат' +# mapPath: /Maps/Corvax/Shuttles/Nfsd/bigskat.yml +# minPlayers: 0 +# stations: +# BigSkat: +# stationProto: StandardFrontierSecurityVessel +# components: +# - type: StationNameSetup +# mapNameTemplate: 'Большой скат {1}' +# nameGenerator: +# !type:NanotrasenNameGenerator +# prefixCreator: '14' +# - type: StationJobs +# availableJobs: # PrisonGuard: [ 0, 0 ] - SeniorOfficer: [ 0, 0 ] - Brigmedic: [ 0, 0 ] - NFDetective: [ 0, 0 ] - Deputy: [ 0, 0 ] - Cadet: [ 0, 0 ] - Chef: [ 0, 0 ] - Bailiff: [ 0, 0 ] +# SeniorOfficer: [ 0, 0 ] +# Brigmedic: [ 0, 0 ] +# NFDetective: [ 0, 0 ] +# Deputy: [ 0, 0 ] +# Cadet: [ 0, 0 ] +# Chef: [ 0, 0 ] +# Bailiff: [ 0, 0 ]