diff --git a/Content.Server/ADT/Administration/Commands/SendUpdateServerCommand.cs b/Content.Server/ADT/Administration/Commands/SendUpdateServerCommand.cs new file mode 100644 index 00000000000..74c294afda1 --- /dev/null +++ b/Content.Server/ADT/Administration/Commands/SendUpdateServerCommand.cs @@ -0,0 +1,26 @@ +using Content.Shared.Administration; +using Robust.Shared.Console; +using Content.Server.Administration; +using Content.Server.ServerUpdates; + +namespace Content.Server.ADT.Administration.Commands; + + +[AdminCommand(AdminFlags.Permissions)] +public sealed class SendUpdateServerCommand : LocalizedCommands +{ + [Dependency] private readonly ServerUpdateManager _serverManager = default!; + public override string Command => "send_updateserver_devtest"; + + public override async void Execute(IConsoleShell shell, string argStr, string[] args) + { + var player = shell.Player; + if (player == null) + { + shell.WriteError(LocalizationManager.GetString("shell-target-player-does-not-exist")); + return; + } + + _serverManager.SendDiscordWebHookUpdateMessage(); + } +} diff --git a/Content.Server/ServerUpdates/ServerUpdateManager.cs b/Content.Server/ServerUpdates/ServerUpdateManager.cs index bf18428e25b..a333a7fcccd 100644 --- a/Content.Server/ServerUpdates/ServerUpdateManager.cs +++ b/Content.Server/ServerUpdates/ServerUpdateManager.cs @@ -8,6 +8,9 @@ using Robust.Shared.Enums; using Robust.Shared.Player; using Robust.Shared.Timing; +using Content.Shared.ADT.CCVar; +using Content.Server.Discord; +using Content.Server.GameTicking; namespace Content.Server.ServerUpdates; @@ -27,6 +30,8 @@ public sealed class ServerUpdateManager : IPostInjectInit [Dependency] private readonly IBaseServer _server = default!; [Dependency] private readonly IConfigurationManager _cfg = default!; [Dependency] private readonly ILogManager _logManager = default!; + [Dependency] private readonly DiscordWebhook _discord = default!; + [Dependency] private readonly IEntitySystemManager _entitySystemManager = default!; private ISawmill _sawmill = default!; @@ -102,6 +107,7 @@ private void WatchdogOnUpdateReceived() _chatManager.DispatchServerAnnouncement(Loc.GetString("server-updates-received")); _updateOnRoundEnd = true; ServerEmptyUpdateRestartCheck("update notification"); + SendDiscordWebHookUpdateMessage(); // ADT-Tweak } /// @@ -148,4 +154,87 @@ void IPostInjectInit.PostInject() { _sawmill = _logManager.GetSawmill("restart"); } + // ADT-Tweak-start: Отправка сообщения в Discord при обновлении сервера + public async void SendDiscordWebHookUpdateMessage() + { + if (!string.IsNullOrWhiteSpace(_cfg.GetCVar(ADTDiscordWebhookCCVars.DiscordServerUpdateWebhook))) + { + var webhookUrl = _cfg.GetCVar(ADTDiscordWebhookCCVars.DiscordServerUpdateWebhook); + if (webhookUrl == null) + return; + + if (await _discord.GetWebhook(webhookUrl) is not { } webhookData) + return; + + // Получение данных сервера + var serverName = _cfg.GetCVar("game.hostname"); + var serverDesc = _cfg.GetCVar("game.desc"); + var engineVersion = _cfg.GetCVar("build.engine_version"); + var buildVersion = _cfg.GetCVar("build.version"); + + // Сообщение о перезапуске сервера + var descContent = "Обновление получено, сервер автоматически перезапустится для обновления в конце этого раунда."; + + // Определение состояния раунда + var gameTicker = _entitySystemManager.GetEntitySystem(); + var roundDescription = gameTicker.RunLevel switch + { + GameRunLevel.PreRoundLobby => gameTicker.RoundId == 0 + ? "pre-round lobby after server restart" + : $"pre-round lobby for round {gameTicker.RoundId + 1}", + GameRunLevel.InRound => $"round {gameTicker.RoundId}", + GameRunLevel.PostRound => $"post-round {gameTicker.RoundId}", + _ => throw new ArgumentOutOfRangeException(nameof(gameTicker.RunLevel), $"{gameTicker.RunLevel} was not matched."), + }; + + // Формирование структуры embed + var embed = new WebhookEmbed + { + Title = "Обновление пришло", + Description = descContent, + Color = 0x0e9c00, + Footer = new WebhookEmbedFooter + { + Text = $"{serverName} ({roundDescription})" + }, + Fields = new List() + }; + + // Добавление полей только если они не пустые + AddIfNotEmpty(embed.Fields, "Название сервера", serverName); + AddIfNotEmpty(embed.Fields, "Описание сервера", serverDesc); + AddIfNotEmpty(embed.Fields, "RobustToolbox version", engineVersion); + AddIfNotEmpty(embed.Fields, "Build version", buildVersion); + + // Формирование полезной нагрузки + var payload = new WebhookPayload + { + Embeds = new List { embed }, + Username = Loc.GetString("username-webhook-update") + }; + + // Проверка, нужно ли добавлять пинг + var shouldPingOnUpdate = _cfg.GetCVar(ADTDiscordWebhookCCVars.ShouldPingOnUpdate); + if (shouldPingOnUpdate) + { + // Добавляем пинг в поле Content. Это будет сообщение, которое будет сверху + payload.Content = "<@&1275740664264659017>"; // ID роли "Обновления" + } + + // Отправка сообщения в Discord + var identifier = webhookData.ToIdentifier(); + payload.AllowedMentions.AllowRoleMentions(); + await _discord.CreateMessage(identifier, payload); + } + } + + // Вспомогательный метод для добавления полей в embed + private void AddIfNotEmpty(List fields, string fieldName, string? fieldValue) + { + if (!string.IsNullOrWhiteSpace(fieldValue)) + { + fields.Add(new WebhookEmbedField { Name = fieldName, Value = fieldValue, Inline = true }); + } + } + // ADT-Tweak-end } diff --git a/Content.Shared/ADT/CCVar/CCVars.WebhookDiscord.cs b/Content.Shared/ADT/CCVar/CCVars.WebhookDiscord.cs index 3924fee9abb..e27a7c4a2ba 100644 --- a/Content.Shared/ADT/CCVar/CCVars.WebhookDiscord.cs +++ b/Content.Shared/ADT/CCVar/CCVars.WebhookDiscord.cs @@ -10,5 +10,19 @@ public sealed class ADTDiscordWebhookCCVars : CVars /// URL of the Discord webhook which will relay adminwho info to the channel. /// public static readonly CVarDef DiscordAdminwhoWebhook = - CVarDef.Create("discord.adminwho_webhook", string.Empty, CVar.SERVERONLY | CVar.CONFIDENTIAL); + CVarDef.Create("discord.adminwho_webhook", string.Empty, CVar.SERVERONLY | CVar.CONFIDENTIAL | CVar.ARCHIVE); + + /// + /// This constant specifies a webhook that will send a message to Discord when a server updates. + /// + public static readonly CVarDef DiscordServerUpdateWebhook = + CVarDef.Create("discord.server_update_webhook", string.Empty, CVar.SERVERONLY | CVar.CONFIDENTIAL | CVar.ARCHIVE); + + /// + /// This constant specifies whether a ping should be sent to a specific Discord role + /// when the server update notification is triggered. If set to true, a ping will be sent to the role. + /// If set to false, no ping will be sent. + /// + public static readonly CVarDef ShouldPingOnUpdate = + CVarDef.Create("discord.server_update_webhook_ping", true, CVar.SERVERONLY | CVar.ARCHIVE); }