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);
}