using Content.Server.Discord; using Content.Shared._NF.CCVar; using Content.Server.Maps; using Content.Shared.GameTicking; using Robust.Shared; using Robust.Shared.Configuration; using Content.Server._NF.RoundNotifications.Events; namespace Content.Server._NF.RoundNotifications.Systems; /// /// Listen for game events and send notifications to Discord. /// /// /// Updated version of the old Nyanotrasen RoundNotificationsSystem /// public sealed class RoundNotificationsSystem : EntitySystem { [Dependency] private readonly IConfigurationManager _config = default!; [Dependency] private readonly IGameMapManager _gameMapManager = default!; [Dependency] private readonly DiscordWebhook _discord = default!; private ISawmill _sawmill = default!; private string _roleId = string.Empty; private bool _roundStartOnly; private string _serverName = string.Empty; private WebhookIdentifier? _webhookIdentifier; /// public override void Initialize() { base.Initialize(); SubscribeLocalEvent(OnRoundRestart); SubscribeLocalEvent(OnRoundStarted); SubscribeLocalEvent(OnRoundEnded); Subs.CVar(_config, CVars.GameHostName, value => _serverName = value, true); Subs.CVar(_config, NFCCVars.DiscordRoundRoleId, value => _roleId = value, true); Subs.CVar(_config, NFCCVars.DiscordRoundStartOnly, value => _roundStartOnly = value, true); Subs.CVar(_config, NFCCVars.DiscordRoundWebhook, value => { if (!string.IsNullOrWhiteSpace(value)) _discord.GetWebhook(value, data => _webhookIdentifier = data.ToIdentifier()); else _webhookIdentifier = null; }, true); _sawmill = Logger.GetSawmill("notifications"); } private void OnRoundRestart(RoundRestartCleanupEvent e) { if (_webhookIdentifier == null) return; var text = Loc.GetString("discord-round-new"); SendDiscordMessage(text, true, 0x91B2C7); } private void OnRoundStarted(RoundStartedEvent e) { if (_webhookIdentifier == null) return; var map = _gameMapManager.GetSelectedMap(); var mapName = map?.MapName ?? Loc.GetString("discord-round-unknown-map"); var text = Loc.GetString("discord-round-start", ("id", e.RoundId), ("map", mapName)); SendDiscordMessage(text, false); } private void OnRoundEnded(RoundEndMessageEvent e) { if (_webhookIdentifier == null || _roundStartOnly) return; var text = Loc.GetString("discord-round-end", ("id", e.RoundId), ("hours", Math.Truncate(e.RoundDuration.TotalHours)), ("minutes", e.RoundDuration.Minutes), ("seconds", e.RoundDuration.Seconds)); SendDiscordMessage(text, false, 0xB22B27); } private async void SendDiscordMessage(string text, bool ping = false, int color = 0x41F097) { if (_webhookIdentifier == null) return; try { // Limit server name to 1500 characters, in case someone tries to be a little funny var serverName = _serverName[..Math.Min(_serverName.Length, 1500)]; var message = ""; if (!string.IsNullOrEmpty(_roleId) && ping) message = $"<@&{_roleId}>"; // Build the embed var payload = new WebhookPayload { Content = message, Embeds = new List { new() { Title = Loc.GetString("discord-round-title"), Description = text, Color = color, Footer = new WebhookEmbedFooter { Text = $"{serverName}" }, }, }, }; if (!string.IsNullOrEmpty(_roleId) && ping) { var mentions = new WebhookMentions(); mentions.Roles.Add(_roleId); payload.AllowedMentions = mentions; } var request = await _discord.CreateMessage(_webhookIdentifier.Value, payload); if (!request.IsSuccessStatusCode) { var content = await request.Content.ReadAsStringAsync(); _sawmill.Error($"Discord returned bad status code when posting message: {request.StatusCode}\nResponse: {content}"); return; } } catch (Exception e) { _sawmill.Error($"Error while sending discord round status message:\n{e}"); } } }