Skip to content

Commit

Permalink
Merge branch 'master' into MedicalPodRebalance
Browse files Browse the repository at this point in the history
  • Loading branch information
Chocosasel authored Nov 15, 2024
2 parents 53cd9b0 + 71ada99 commit e5a8a92
Show file tree
Hide file tree
Showing 3 changed files with 228 additions and 34 deletions.
45 changes: 44 additions & 1 deletion Content.Server/Administration/ServerApi.cs
Original file line number Diff line number Diff line change
Expand Up @@ -6,19 +6,23 @@
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;
using Content.Shared.Prototypes;
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;
Expand Down Expand Up @@ -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!;
Expand All @@ -73,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);
Expand Down Expand Up @@ -395,6 +404,35 @@ await RunOnMainThread(async () =>
});
}

private async Task ActionAhelpSend(IStatusHandlerContext context, Actor actor)
{
var body = await ReadJson<DiscordAhelpBody>(context);
if (body == null)
return;
if (body.Text == null)
{
await context.RespondErrorAsync(HttpStatusCode.BadRequest);
return;
}
var bwoinkSystem = _entitySystemManager.GetEntitySystem<BwoinkSystem>();
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
Expand Down Expand Up @@ -603,7 +641,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; }
Expand Down
153 changes: 152 additions & 1 deletion Content.Server/Administration/Systems/BwoinkSystem.cs
Original file line number Diff line number Diff line change
Expand Up @@ -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();
Expand Down Expand Up @@ -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<string>();

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; }
Expand Down
64 changes: 32 additions & 32 deletions Resources/Prototypes/Corvax/Shipyard/Nfsd/bigskat.yml
Original file line number Diff line number Diff line change
@@ -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 ]

0 comments on commit e5a8a92

Please sign in to comment.