[feat] Discord hooks
# Conflicts: # Content.Server/GameTicking/GameTicker.RoundFlow.cs
This commit is contained in:
@@ -1,3 +1,4 @@
|
|||||||
|
using System.Collections.ObjectModel;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using Content.Server.Announcements;
|
using Content.Server.Announcements;
|
||||||
using Content.Server.Discord;
|
using Content.Server.Discord;
|
||||||
@@ -158,7 +159,10 @@ namespace Content.Server.GameTicking
|
|||||||
var ev = new PreGameMapLoad(targetMapId, map, loadOpts);
|
var ev = new PreGameMapLoad(targetMapId, map, loadOpts);
|
||||||
RaiseLocalEvent(ev);
|
RaiseLocalEvent(ev);
|
||||||
|
|
||||||
var gridIds = _map.LoadMap(targetMapId, ev.GameMap.MapPath.ToString(), ev.Options);
|
if (!_map.TryLoad(targetMapId, ev.GameMap.MapPath.ToString(), out var gridIds, ev.Options))
|
||||||
|
{
|
||||||
|
return new Collection<EntityUid>();
|
||||||
|
}
|
||||||
|
|
||||||
_metaData.SetEntityName(_mapManager.GetMapEntityId(targetMapId), "Station map");
|
_metaData.SetEntityName(_mapManager.GetMapEntityId(targetMapId), "Station map");
|
||||||
|
|
||||||
@@ -250,7 +254,7 @@ namespace Content.Server.GameTicking
|
|||||||
UpdateLateJoinStatus();
|
UpdateLateJoinStatus();
|
||||||
AnnounceRound();
|
AnnounceRound();
|
||||||
UpdateInfoText();
|
UpdateInfoText();
|
||||||
SendRoundStartedDiscordMessage();
|
RaiseLocalEvent(new RoundStartedEvent(RoundId)); // WD-EDIT
|
||||||
|
|
||||||
#if EXCEPTION_TOLERANCE
|
#if EXCEPTION_TOLERANCE
|
||||||
}
|
}
|
||||||
@@ -304,7 +308,6 @@ namespace Content.Server.GameTicking
|
|||||||
LobbySong = _robustRandom.Pick(_lobbyMusicCollection.PickFiles).ToString();
|
LobbySong = _robustRandom.Pick(_lobbyMusicCollection.PickFiles).ToString();
|
||||||
|
|
||||||
ShowRoundEndScoreboard(text);
|
ShowRoundEndScoreboard(text);
|
||||||
SendRoundEndDiscordMessage();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public void ShowRoundEndScoreboard(string text = "")
|
public void ShowRoundEndScoreboard(string text = "")
|
||||||
@@ -360,7 +363,7 @@ namespace Content.Server.GameTicking
|
|||||||
|
|
||||||
if (TryGetEntity(mind.OriginalOwnedEntity, out var entity))
|
if (TryGetEntity(mind.OriginalOwnedEntity, out var entity))
|
||||||
{
|
{
|
||||||
_pvsOverride.AddGlobalOverride(GetNetEntity(entity.Value), recursive: true);
|
_pvsOverride.AddGlobalOverride(entity.Value);
|
||||||
}
|
}
|
||||||
|
|
||||||
var roles = _roles.MindGetAllRoles(mindId);
|
var roles = _roles.MindGetAllRoles(mindId);
|
||||||
@@ -388,38 +391,7 @@ namespace Content.Server.GameTicking
|
|||||||
|
|
||||||
RaiseNetworkEvent(new RoundEndMessageEvent(gamemodeTitle, roundEndText, roundDuration, RoundId,
|
RaiseNetworkEvent(new RoundEndMessageEvent(gamemodeTitle, roundEndText, roundDuration, RoundId,
|
||||||
listOfPlayerInfoFinal.Length, listOfPlayerInfoFinal, LobbySong));
|
listOfPlayerInfoFinal.Length, listOfPlayerInfoFinal, LobbySong));
|
||||||
}
|
RaiseLocalEvent(new RoundEndedEvent(RoundId, roundDuration)); // WD-EDIT
|
||||||
|
|
||||||
private async void SendRoundEndDiscordMessage()
|
|
||||||
{
|
|
||||||
try
|
|
||||||
{
|
|
||||||
if (_webhookIdentifier == null)
|
|
||||||
return;
|
|
||||||
|
|
||||||
var duration = RoundDuration();
|
|
||||||
var content = Loc.GetString("discord-round-notifications-end",
|
|
||||||
("id", RoundId),
|
|
||||||
("hours", Math.Truncate(duration.TotalHours)),
|
|
||||||
("minutes", duration.Minutes),
|
|
||||||
("seconds", duration.Seconds));
|
|
||||||
var payload = new WebhookPayload { Content = content };
|
|
||||||
|
|
||||||
await _discord.CreateMessage(_webhookIdentifier.Value, payload);
|
|
||||||
|
|
||||||
if (DiscordRoundEndRole == null)
|
|
||||||
return;
|
|
||||||
|
|
||||||
content = Loc.GetString("discord-round-notifications-end-ping", ("roleId", DiscordRoundEndRole));
|
|
||||||
payload = new WebhookPayload { Content = content };
|
|
||||||
payload.AllowedMentions.AllowRoleMentions();
|
|
||||||
|
|
||||||
await _discord.CreateMessage(_webhookIdentifier.Value, payload);
|
|
||||||
}
|
|
||||||
catch (Exception e)
|
|
||||||
{
|
|
||||||
Log.Error($"Error while sending discord round end message:\n{e}");
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public void RestartRound()
|
public void RestartRound()
|
||||||
|
|||||||
13
Content.Server/White/Discord/GameTicking/RoundEndedEvent.cs
Normal file
13
Content.Server/White/Discord/GameTicking/RoundEndedEvent.cs
Normal file
@@ -0,0 +1,13 @@
|
|||||||
|
namespace Content.Shared.GameTicking;
|
||||||
|
|
||||||
|
public sealed class RoundEndedEvent : EntityEventArgs
|
||||||
|
{
|
||||||
|
public int RoundId { get; }
|
||||||
|
public TimeSpan RoundDuration { get; }
|
||||||
|
|
||||||
|
public RoundEndedEvent(int roundId, TimeSpan roundDuration)
|
||||||
|
{
|
||||||
|
RoundId = roundId;
|
||||||
|
RoundDuration = roundDuration;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,11 @@
|
|||||||
|
namespace Content.Shared.GameTicking;
|
||||||
|
|
||||||
|
public sealed class RoundStartedEvent : EntityEventArgs
|
||||||
|
{
|
||||||
|
public int RoundId { get; }
|
||||||
|
|
||||||
|
public RoundStartedEvent(int roundId)
|
||||||
|
{
|
||||||
|
RoundId = roundId;
|
||||||
|
}
|
||||||
|
}
|
||||||
125
Content.Server/White/Discord/RoundNotificationsSystem.cs
Normal file
125
Content.Server/White/Discord/RoundNotificationsSystem.cs
Normal file
@@ -0,0 +1,125 @@
|
|||||||
|
using System.Net.Http;
|
||||||
|
using System.Text;
|
||||||
|
using System.Text.Json;
|
||||||
|
using System.Text.Json.Serialization;
|
||||||
|
using Content.Server.Maps;
|
||||||
|
using Content.Shared.GameTicking;
|
||||||
|
using Content.Shared.White;
|
||||||
|
using Robust.Shared.Configuration;
|
||||||
|
|
||||||
|
namespace Content.Server.Corvax.RoundNotifications;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Listen game events and send notifications to Discord
|
||||||
|
/// </summary>
|
||||||
|
public sealed class RoundNotificationsSystem : EntitySystem
|
||||||
|
{
|
||||||
|
[Dependency] private readonly IConfigurationManager _config = default!;
|
||||||
|
[Dependency] private readonly IGameMapManager _gameMapManager = default!;
|
||||||
|
|
||||||
|
private ISawmill _sawmill = default!;
|
||||||
|
private readonly HttpClient _httpClient = new();
|
||||||
|
|
||||||
|
private string _webhookUrl = String.Empty;
|
||||||
|
private string _roleId = String.Empty;
|
||||||
|
private bool _roundStartOnly;
|
||||||
|
|
||||||
|
/// <inheritdoc/>
|
||||||
|
public override void Initialize()
|
||||||
|
{
|
||||||
|
SubscribeLocalEvent<RoundRestartCleanupEvent>(OnRoundRestart);
|
||||||
|
SubscribeLocalEvent<RoundStartedEvent>(OnRoundStarted);
|
||||||
|
SubscribeLocalEvent<RoundEndedEvent>(OnRoundEnded);
|
||||||
|
|
||||||
|
_config.OnValueChanged(WhiteCVars.DiscordRoundWebhook, value => _webhookUrl = value, true);
|
||||||
|
_config.OnValueChanged(WhiteCVars.DiscordRoundRoleId, value => _roleId = value, true);
|
||||||
|
_config.OnValueChanged(WhiteCVars.DiscordRoundStartOnly, value => _roundStartOnly = value, true);
|
||||||
|
|
||||||
|
_sawmill = IoCManager.Resolve<ILogManager>().GetSawmill("notifications");
|
||||||
|
}
|
||||||
|
|
||||||
|
private void OnRoundRestart(RoundRestartCleanupEvent e)
|
||||||
|
{
|
||||||
|
if (String.IsNullOrEmpty(_webhookUrl))
|
||||||
|
return;
|
||||||
|
|
||||||
|
var payload = new WebhookPayload()
|
||||||
|
{
|
||||||
|
Content = Loc.GetString("discord-round-new"),
|
||||||
|
};
|
||||||
|
|
||||||
|
if (!String.IsNullOrEmpty(_roleId))
|
||||||
|
{
|
||||||
|
payload = new WebhookPayload()
|
||||||
|
{
|
||||||
|
Content = $"<@&{_roleId}> {Loc.GetString("discord-round-new")}",
|
||||||
|
AllowedMentions = new Dictionary<string, string[]>
|
||||||
|
{
|
||||||
|
{ "roles", new []{ _roleId } }
|
||||||
|
},
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
SendDiscordMessage(payload);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void OnRoundStarted(RoundStartedEvent e)
|
||||||
|
{
|
||||||
|
if (String.IsNullOrEmpty(_webhookUrl))
|
||||||
|
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));
|
||||||
|
var payload = new WebhookPayload() { Content = text };
|
||||||
|
|
||||||
|
SendDiscordMessage(payload);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void OnRoundEnded(RoundEndedEvent e)
|
||||||
|
{
|
||||||
|
if (String.IsNullOrEmpty(_webhookUrl) || _roundStartOnly)
|
||||||
|
return;
|
||||||
|
|
||||||
|
var text = Loc.GetString("discord-round-end",
|
||||||
|
("id", e.RoundId),
|
||||||
|
("hours", e.RoundDuration.Hours),
|
||||||
|
("minutes", e.RoundDuration.Minutes),
|
||||||
|
("seconds", e.RoundDuration.Seconds));
|
||||||
|
var payload = new WebhookPayload() { Content = text };
|
||||||
|
|
||||||
|
SendDiscordMessage(payload);
|
||||||
|
}
|
||||||
|
|
||||||
|
private async void SendDiscordMessage(WebhookPayload payload)
|
||||||
|
{
|
||||||
|
var request = await _httpClient.PostAsync(_webhookUrl,
|
||||||
|
new StringContent(JsonSerializer.Serialize(payload), Encoding.UTF8, "application/json"));
|
||||||
|
|
||||||
|
var content = await request.Content.ReadAsStringAsync();
|
||||||
|
if (!request.IsSuccessStatusCode)
|
||||||
|
{
|
||||||
|
_sawmill.Log(LogLevel.Error, $"Discord returned bad status code when posting message: {request.StatusCode}\nResponse: {content}");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private struct WebhookPayload
|
||||||
|
{
|
||||||
|
[JsonPropertyName("content")]
|
||||||
|
public string Content { get; set; } = "";
|
||||||
|
|
||||||
|
[JsonPropertyName("allowed_mentions")]
|
||||||
|
public Dictionary<string, string[]> AllowedMentions { get; set; } =
|
||||||
|
new()
|
||||||
|
{
|
||||||
|
{ "parse", Array.Empty<string>() }
|
||||||
|
};
|
||||||
|
|
||||||
|
public WebhookPayload()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -34,4 +34,26 @@ public sealed class WhiteCVars
|
|||||||
QueueEnabled = CVarDef.Create("queue.enabled", false, CVar.SERVERONLY);
|
QueueEnabled = CVarDef.Create("queue.enabled", false, CVar.SERVERONLY);
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
* RoundNotifications
|
||||||
|
*/
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// URL of the Discord webhook which will send round status notifications.
|
||||||
|
/// </summary>
|
||||||
|
public static readonly CVarDef<string> DiscordRoundWebhook =
|
||||||
|
CVarDef.Create("discord.round_webhook", string.Empty, CVar.SERVERONLY);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Discord ID of role which will be pinged on new round start message.
|
||||||
|
/// </summary>
|
||||||
|
public static readonly CVarDef<string> DiscordRoundRoleId =
|
||||||
|
CVarDef.Create("discord.round_roleid", string.Empty, CVar.SERVERONLY);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Send notifications only about a new round begins.
|
||||||
|
/// </summary>
|
||||||
|
public static readonly CVarDef<bool> DiscordRoundStartOnly =
|
||||||
|
CVarDef.Create("discord.round_start_only", false, CVar.SERVERONLY);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user