Skip to content

Commit

Permalink
AHelps: The Configuration Update (#1022)
Browse files Browse the repository at this point in the history
Co-authored-by: Aiden <[email protected]>
  • Loading branch information
sleepyyapril and Aidenkrz committed Dec 17, 2024
1 parent 68ae3bc commit 1eac257
Show file tree
Hide file tree
Showing 3 changed files with 173 additions and 62 deletions.
2 changes: 2 additions & 0 deletions Content.Server/Administration/ServerApi.cs
Original file line number Diff line number Diff line change
Expand Up @@ -676,6 +676,8 @@ public sealed class BwoinkActionBody
public required Guid Guid { get; init; }
public bool UserOnly { get; init; }
public required bool WebhookUpdate { get; init; }
public required string RoleName { get; init; }
public required string RoleColor { get; init; }
}

#endregion
Expand Down
185 changes: 123 additions & 62 deletions Content.Server/Administration/Systems/BwoinkSystem.cs
Original file line number Diff line number Diff line change
Expand Up @@ -11,12 +11,14 @@
using Content.Server.Discord;
using Content.Server.GameTicking;
using Content.Server.Players.RateLimiting;
using Content.Server.Preferences.Managers;
using Content.Shared.Administration;
using Content.Shared.CCVar;
using Content.Shared.GameTicking;
using Content.Shared.Mind;
using Content.Shared.Players.RateLimiting;
using JetBrains.Annotations;
using Pidgin.Configuration;
using Robust.Server.Player;
using Robust.Shared;
using Robust.Shared.Configuration;
Expand All @@ -43,6 +45,7 @@ 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 IServerPreferencesManager _preferencesManager = default!;

[GeneratedRegex(@"^https://(?:(?:canary|ptb)\.)?discord\.com/api/webhooks/(\d+)/((?!.*/).*)$")]
private static partial Regex DiscordRegex();
Expand Down Expand Up @@ -635,8 +638,18 @@ public void OnWebhookBwoinkTextMessage(BwoinkTextMessage message, ServerApi.Bwoi
// Note for forks:
AdminData webhookAdminData = new();

// TODO: fix args
OnBwoinkInternal(message, SystemUserId, webhookAdminData, body.Username, null, body.UserOnly, body.WebhookUpdate, true);
var bwoinkParams = new BwoinkParams(
message,
SystemUserId,
webhookAdminData,
body.Username,
null,
body.UserOnly,
body.WebhookUpdate,
true,
body.RoleName,
body.RoleColor);
OnBwoinkInternal(bwoinkParams);
}

protected override void OnBwoinkTextMessage(BwoinkTextMessage message, EntitySessionEventArgs eventArgs)
Expand All @@ -660,64 +673,79 @@ protected override void OnBwoinkTextMessage(BwoinkTextMessage message, EntitySes
if (_rateLimit.CountAction(eventArgs.SenderSession, RateLimitKey) != RateLimitStatus.Allowed)
return;

OnBwoinkInternal(message, eventArgs.SenderSession.UserId, senderAdmin, eventArgs.SenderSession.Name, eventArgs.SenderSession.Channel, false, true, false);
var bwoinkParams = new BwoinkParams(message,
eventArgs.SenderSession.UserId,
senderAdmin,
eventArgs.SenderSession.Name,
eventArgs.SenderSession.Channel,
false,
true,
false);
OnBwoinkInternal(bwoinkParams);
}

/// <summary>
/// Sends a bwoink. Common to both internal messages (sent via the ahelp or admin interface) and webhook messages (sent through the webhook, e.g. via Discord)
/// </summary>
/// <param name="message">The message being sent.</param>
/// <param name="senderId">The network GUID of the person sending the message.</param>
/// <param name="senderAdmin">The admin privileges of the person sending the message.</param>
/// <param name="senderName">The name of the person sending the message.</param>
/// <param name="senderChannel">The channel to send a message to, e.g. in case of failure to send</param>
/// <param name="sendWebhook">If true, message should be sent off through the webhook if possible</param>
/// <param name="fromWebhook">Message originated from a webhook (e.g. Discord)</param>
private void OnBwoinkInternal(BwoinkTextMessage message, NetUserId senderId, AdminData? senderAdmin, string senderName, INetChannel? senderChannel, bool userOnly, bool sendWebhook, bool fromWebhook)
/// <param name="bwoinkParams">The parameters of the message being sent.</param>
private void OnBwoinkInternal(BwoinkParams bwoinkParams)
{
_activeConversations[message.UserId] = DateTime.Now;
_activeConversations[bwoinkParams.Message.UserId] = DateTime.Now;

var escapedText = FormattedMessage.EscapeText(message.Text);

string bwoinkText;
string adminPrefix = "";
var escapedText = FormattedMessage.EscapeText(bwoinkParams.Message.Text);
var adminColor = _config.GetCVar(CCVars.AdminBwoinkColor);
var adminPrefix = "";
var bwoinkText = $"{bwoinkParams.SenderName}";

//Getting an administrator position
if (_config.GetCVar(CCVars.AhelpAdminPrefix) && senderAdmin is not null && senderAdmin.Title is not null)
if (_config.GetCVar(CCVars.AhelpAdminPrefix))
{
adminPrefix = $"[bold]\\[{senderAdmin.Title}\\][/bold] ";
}
if (bwoinkParams.SenderAdmin is not null && bwoinkParams.SenderAdmin.Title is not null)
adminPrefix = $"[bold]\\[{bwoinkParams.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}{senderName}[/color]";
if (_config.GetCVar(CCVars.UseDiscordRoleName) && bwoinkParams.RoleName is not null)
adminPrefix = $"[bold]\\[{bwoinkParams.RoleName}\\][/bold] ";
}
else if (fromWebhook || senderAdmin is not null && senderAdmin.HasFlag(AdminFlags.Adminhelp)) // Frontier: anything sent via webhooks are from an admin.

// If role color is enabled and exists, use it, otherwise use the discord reply color
if (_config.GetCVar(CCVars.DiscordReplyColor) != string.Empty && bwoinkParams.FromWebhook)
adminColor = _config.GetCVar(CCVars.DiscordReplyColor);

if (_config.GetCVar(CCVars.UseDiscordRoleColor) && bwoinkParams.RoleColor is not null)
adminColor = bwoinkParams.RoleColor;

if (!bwoinkParams.FromWebhook
&& _config.GetCVar(CCVars.UseAdminOOCColorInBwoinks)
&& bwoinkParams.SenderAdmin is not null)
{
bwoinkText = $"[color=red]{adminPrefix}{senderName}[/color]";
var prefs = _preferencesManager.GetPreferences(bwoinkParams.SenderId);
adminColor = prefs.AdminOOCColor.ToHex();
}
else

if (bwoinkParams.SenderAdmin is not null)
{
bwoinkText = $"{senderName}";
if (bwoinkParams.SenderAdmin.Flags ==
AdminFlags.Adminhelp) // Mentor. Not full admin. That's why it's colored differently.
bwoinkText = $"[color=purple]{adminPrefix}{bwoinkParams.SenderName}[/color]";
else if (bwoinkParams.FromWebhook || bwoinkParams.SenderAdmin.HasFlag(AdminFlags.Adminhelp)) // Frontier: anything sent via webhooks are from an admin.
bwoinkText = $"[color={adminColor}]{adminPrefix}{bwoinkParams.SenderName}[/color]";
}

if (fromWebhook)
bwoinkText = $"(DISCORD) {bwoinkText}";
if (bwoinkParams.FromWebhook)
bwoinkText = $"{_config.GetCVar(CCVars.DiscordReplyPrefix)}{bwoinkText}";

bwoinkText = $"{(message.PlaySound ? "" : "(S) ")}{bwoinkText}: {escapedText}";
bwoinkText = $"{(bwoinkParams.Message.PlaySound ? "" : "(S) ")}{bwoinkText}: {escapedText}";

// If it's not an admin / admin chooses to keep the sound then play it.
var playSound = senderAdmin == null || message.PlaySound;
var msg = new BwoinkTextMessage(message.UserId, senderId, bwoinkText, playSound: playSound);
var playSound = bwoinkParams.SenderAdmin == null || bwoinkParams.Message.PlaySound;
var msg = new BwoinkTextMessage(bwoinkParams.Message.UserId, bwoinkParams.SenderId, bwoinkText, playSound: playSound);

LogBwoink(msg);

var admins = GetTargetAdmins();

// Notify all admins
if (!userOnly)
if (!bwoinkParams.UserOnly)
{
foreach (var channel in admins)
{
Expand All @@ -727,13 +755,13 @@ private void OnBwoinkInternal(BwoinkTextMessage message, NetUserId senderId, Adm

string adminPrefixWebhook = "";

if (_config.GetCVar(CCVars.AhelpAdminPrefixWebhook) && senderAdmin is not null && senderAdmin.Title is not null)
if (_config.GetCVar(CCVars.AhelpAdminPrefixWebhook) && bwoinkParams.SenderAdmin is not null && bwoinkParams.SenderAdmin.Title is not null)
{
adminPrefixWebhook = $"[bold]\\[{senderAdmin.Title}\\][/bold] ";
adminPrefixWebhook = $"[bold]\\[{bwoinkParams.SenderAdmin.Title}\\][/bold] ";
}

// Notify player
if (_playerManager.TryGetSessionById(message.UserId, out var session))
if (_playerManager.TryGetSessionById(bwoinkParams.Message.UserId, out var session))
{
if (!admins.Contains(session.Channel))
{
Expand All @@ -742,28 +770,22 @@ private void OnBwoinkInternal(BwoinkTextMessage message, NetUserId senderId, Adm
{
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 ==
if (bwoinkParams.SenderAdmin is not null &&
bwoinkParams.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))
{
else if (bwoinkParams.SenderAdmin is not null && bwoinkParams.SenderAdmin.HasFlag(AdminFlags.Adminhelp))
overrideMsgText = $"[color=red]{adminPrefixWebhook}{_overrideClientName}[/color]";
}
else
{
overrideMsgText = $"{senderName}"; // Not an admin, name is not overridden.
}
overrideMsgText = $"{bwoinkParams.SenderName}"; // Not an admin, name is not overridden.

if (fromWebhook)
overrideMsgText = $"(DC) {overrideMsgText}";
if (bwoinkParams.FromWebhook)
overrideMsgText = $"{_config.GetCVar(CCVars.DiscordReplyPrefix)}{overrideMsgText}";

overrideMsgText = $"{(message.PlaySound ? "" : "(S) ")}{overrideMsgText}: {escapedText}";
overrideMsgText = $"{(bwoinkParams.Message.PlaySound ? "" : "(S) ")}{overrideMsgText}: {escapedText}";

RaiseNetworkEvent(new BwoinkTextMessage(message.UserId,
senderId,
RaiseNetworkEvent(new BwoinkTextMessage(bwoinkParams.Message.UserId,
bwoinkParams.SenderId,
overrideMsgText,
playSound: playSound),
session.Channel);
Expand All @@ -774,13 +796,13 @@ private void OnBwoinkInternal(BwoinkTextMessage message, NetUserId senderId, Adm
}

var sendsWebhook = _webhookUrl != string.Empty;
if (sendsWebhook && sendWebhook)
if (sendsWebhook && bwoinkParams.SendWebhook)
{
if (!_messageQueues.ContainsKey(msg.UserId))
_messageQueues[msg.UserId] = new Queue<DiscordRelayedData>();

var str = message.Text;
var unameLength = senderName.Length;
var str = bwoinkParams.Message.Text;
var unameLength = bwoinkParams.SenderName.Length;

if (unameLength + str.Length + _maxAdditionalChars > DescriptionMax)
{
Expand All @@ -789,13 +811,13 @@ private void OnBwoinkInternal(BwoinkTextMessage message, NetUserId senderId, Adm

var nonAfkAdmins = GetNonAfkAdmins();
var messageParams = new AHelpMessageParams(
senderName,
bwoinkParams.SenderName,
str,
senderId != message.UserId,
bwoinkParams.SenderId != bwoinkParams.Message.UserId,
_gameTicker.RoundDuration().ToString("hh\\:mm\\:ss"),
_gameTicker.RunLevel,
playedSound: playSound,
isDiscord: fromWebhook,
isDiscord: bwoinkParams.FromWebhook,
noReceivers: nonAfkAdmins.Count == 0
);
_messageQueues[msg.UserId].Enqueue(GenerateAHelpMessage(messageParams));
Expand All @@ -805,11 +827,11 @@ private void OnBwoinkInternal(BwoinkTextMessage message, NetUserId senderId, Adm
return;

// No admin online, let the player know
if (senderChannel != null)
if (bwoinkParams.SenderChannel != null)
{
var systemText = Loc.GetString("bwoink-system-starmute-message-no-other-users");
var starMuteMsg = new BwoinkTextMessage(message.UserId, SystemUserId, systemText);
RaiseNetworkEvent(starMuteMsg, senderChannel);
var starMuteMsg = new BwoinkTextMessage(bwoinkParams.Message.UserId, SystemUserId, systemText);
RaiseNetworkEvent(starMuteMsg, bwoinkParams.SenderChannel);
}
}
// End Frontier:
Expand All @@ -833,6 +855,7 @@ private IList<INetChannel> GetTargetAdmins()

private static DiscordRelayedData GenerateAHelpMessage(AHelpMessageParams parameters)
{
var config = IoCManager.Resolve<IConfigurationManager>();
var stringbuilder = new StringBuilder();

if (parameters.Icon != null)
Expand All @@ -850,7 +873,7 @@ private static DiscordRelayedData GenerateAHelpMessage(AHelpMessageParams parame
stringbuilder.Append(" **(S)**");

if (parameters.IsDiscord) // Frontier - Discord Indicator
stringbuilder.Append(" **(DC)**");
stringbuilder.Append($" **{config.GetCVar(CCVars.DiscordReplyPrefix)}**");

if (parameters.Icon == null)
stringbuilder.Append($" **{parameters.Username}:** ");
Expand Down Expand Up @@ -941,6 +964,44 @@ public AHelpMessageParams(
}
}

public sealed class BwoinkParams
{
public SharedBwoinkSystem.BwoinkTextMessage Message { get; set; }
public NetUserId SenderId { get; set; }
public AdminData? SenderAdmin { get; set; }
public string SenderName { get; set; }
public INetChannel? SenderChannel { get; set; }
public bool UserOnly { get; set; }
public bool SendWebhook { get; set; }
public bool FromWebhook { get; set; }
public string? RoleName { get; set; }
public string? RoleColor { get; set; }

public BwoinkParams(
SharedBwoinkSystem.BwoinkTextMessage message,
NetUserId senderId,
AdminData? senderAdmin,
string senderName,
INetChannel? senderChannel,
bool userOnly,
bool sendWebhook,
bool fromWebhook,
string? roleName = null,
string? roleColor = null)
{
Message = message;
SenderId = senderId;
SenderAdmin = senderAdmin;
SenderName = senderName;
SenderChannel = senderChannel;
UserOnly = userOnly;
SendWebhook = sendWebhook;
FromWebhook = fromWebhook;
RoleName = roleName;
RoleColor = roleColor;
}
}

public enum PlayerStatusType
{
Connected,
Expand Down
48 changes: 48 additions & 0 deletions Content.Shared/CCVar/CCVars.Admin.Ahelp.cs
Original file line number Diff line number Diff line change
Expand Up @@ -36,4 +36,52 @@ public sealed partial class CCVars
/// <seealso cref="AhelpAdminPrefix"/>
public static readonly CVarDef<bool> AhelpAdminPrefixWebhook =
CVarDef.Create("ahelp.admin_prefix_webhook", false, CVar.SERVERONLY);

/// <summary>
/// If an admin replies to users from discord, should it use their discord role color? (if applicable)
/// Overrides DiscordReplyColor and AdminBwoinkColor.
/// </summary>
/// <seealso cref="DiscordReplyColor"/>
/// <seealso cref="AdminBwoinkColor"/>
public static readonly CVarDef<bool> UseDiscordRoleColor =
CVarDef.Create("ahelp.use_discord_role_color", true, CVar.SERVERONLY);

/// <summary>
/// If an admin replies to users from discord, should it use their discord role name? (if applicable)
/// </summary>
public static readonly CVarDef<bool> UseDiscordRoleName =
CVarDef.Create("ahelp.use_discord_role_name", true, CVar.SERVERONLY);

/// <summary>
/// The text before an admin's name when replying from discord to indicate they're speaking from discord.
/// </summary>
public static readonly CVarDef<string> DiscordReplyPrefix =
CVarDef.Create("ahelp.discord_reply_prefix", "(DISCORD) ", CVar.SERVERONLY);

/// <summary>
/// The color of the names of admins. This is the fallback color for admins.
/// </summary>
/// <seealso cref="UseAdminOOCColorInBwoinks"/>
/// <seealso cref="UseDiscordRoleColor"/>
/// <seealso cref="DiscordReplyColor"/>
public static readonly CVarDef<string> AdminBwoinkColor =
CVarDef.Create("ahelp.admin_bwoink_color", "red", CVar.SERVERONLY);

/// <summary>
/// The color of the names of admins who reply from discord. Leave empty to disable.
/// Unused if UseDiscordRoleColor is true.
/// Overrides AdminBwoinkColor.
/// </summary>
/// <seealso cref="UseDiscordRoleColor"/>
/// <seealso cref="AdminBwoinkColor"/>
public static readonly CVarDef<string> DiscordReplyColor =
CVarDef.Create("ahelp.discord_reply_color", string.Empty, CVar.SERVERONLY);

/// <summary>
/// Use the admin's Admin OOC color in bwoinks.
/// If either the ooc color or this is not set, uses the admin.admin_bwoink_color value.
/// </summary>
/// <seealso cref="AdminBwoinkColor"/>
public static readonly CVarDef<bool> UseAdminOOCColorInBwoinks =
CVarDef.Create("ahelp.bwoink_use_admin_ooc_color", true, CVar.SERVERONLY);
}

0 comments on commit 1eac257

Please sign in to comment.