Station news Discord webhook (#36807)
* Add news article Discord webhook * Send all station articles on round end * Changed event subscrice to RoundEndMessageEvent * Review remarks fix * Added new cvar discord.news_webhook_embed_color Default color taken from news manager console sprite. * Using EntityQueryEnumerator instead of GetStationInMap with TryComp * Extra review remarks fixing * Sorted imports * Added article publication time in embed * Removed markup from article content * Added sorting for articles iteration * Discord hook embed color cvar is string now * Added comment about limits * Added new cvar for posting articles during round * Shitty discord rate limit handling * Fixing copypaste accident Co-authored-by: pathetic meowmeow <uhhadd@gmail.com> * Null initialization of webhook id * SendArticleToDiscordWebhook is non-void now --------- Co-authored-by: Morb0 <14136326+Morb0@users.noreply.github.com> Co-authored-by: pathetic meowmeow <uhhadd@gmail.com>
This commit is contained in:
@@ -1,24 +1,33 @@
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
using Content.Server.Administration.Logs;
|
||||
using Content.Server.CartridgeLoader;
|
||||
using Content.Server.CartridgeLoader.Cartridges;
|
||||
using Content.Server.CartridgeLoader;
|
||||
using Content.Server.Chat.Managers;
|
||||
using Content.Server.Discord;
|
||||
using Content.Server.GameTicking;
|
||||
using Content.Server.MassMedia.Components;
|
||||
using Content.Server.Popups;
|
||||
using Content.Server.Station.Systems;
|
||||
using Content.Shared.Access.Components;
|
||||
using Content.Shared.Access.Systems;
|
||||
using Content.Shared.CartridgeLoader;
|
||||
using Content.Shared.CCVar;
|
||||
using Content.Shared.CartridgeLoader.Cartridges;
|
||||
using Content.Shared.CartridgeLoader;
|
||||
using Content.Shared.Database;
|
||||
using Content.Shared.GameTicking;
|
||||
using Content.Shared.IdentityManagement;
|
||||
using Content.Shared.MassMedia.Components;
|
||||
using Content.Shared.MassMedia.Systems;
|
||||
using Content.Shared.Popups;
|
||||
using Robust.Server.GameObjects;
|
||||
using Robust.Server;
|
||||
using Robust.Shared.Audio.Systems;
|
||||
using Content.Shared.IdentityManagement;
|
||||
using Robust.Shared.Configuration;
|
||||
using Robust.Shared.Maths;
|
||||
using Robust.Shared.Timing;
|
||||
using Robust.Shared.Utility;
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Content.Server.MassMedia.Systems;
|
||||
|
||||
@@ -34,11 +43,36 @@ public sealed class NewsSystem : SharedNewsSystem
|
||||
[Dependency] private readonly StationSystem _station = default!;
|
||||
[Dependency] private readonly GameTicker _ticker = default!;
|
||||
[Dependency] private readonly IChatManager _chatManager = default!;
|
||||
[Dependency] private readonly DiscordWebhook _discord = default!;
|
||||
[Dependency] private readonly IConfigurationManager _cfg = default!;
|
||||
[Dependency] private readonly IBaseServer _baseServer = default!;
|
||||
|
||||
private WebhookIdentifier? _webhookId = null;
|
||||
private Color _webhookEmbedColor;
|
||||
private bool _webhookSendDuringRound;
|
||||
|
||||
public override void Initialize()
|
||||
{
|
||||
base.Initialize();
|
||||
|
||||
// Discord hook
|
||||
_cfg.OnValueChanged(CCVars.DiscordNewsWebhook,
|
||||
value =>
|
||||
{
|
||||
if (!string.IsNullOrWhiteSpace(value))
|
||||
_discord.GetWebhook(value, data => _webhookId = data.ToIdentifier());
|
||||
}, true);
|
||||
|
||||
_cfg.OnValueChanged(CCVars.DiscordNewsWebhookEmbedColor, value =>
|
||||
{
|
||||
_webhookEmbedColor = Color.LawnGreen;
|
||||
if (Color.TryParse(value, out var color))
|
||||
_webhookEmbedColor = color;
|
||||
}, true);
|
||||
|
||||
_cfg.OnValueChanged(CCVars.DiscordNewsWebhookSendDuringRound, value => _webhookSendDuringRound = value, true);
|
||||
SubscribeLocalEvent<RoundEndMessageEvent>(OnRoundEndMessageEvent);
|
||||
|
||||
// News writer
|
||||
SubscribeLocalEvent<NewsWriterComponent, MapInitEvent>(OnMapInit);
|
||||
|
||||
@@ -177,6 +211,9 @@ public sealed class NewsSystem : SharedNewsSystem
|
||||
RaiseLocalEvent(readerUid, ref args);
|
||||
}
|
||||
|
||||
if (_webhookSendDuringRound)
|
||||
Task.Run(async () => await SendArticleToDiscordWebhook(article));
|
||||
|
||||
UpdateWriterDevices();
|
||||
}
|
||||
#endregion
|
||||
@@ -324,4 +361,62 @@ public sealed class NewsSystem : SharedNewsSystem
|
||||
{
|
||||
UpdateWriterUi(ent);
|
||||
}
|
||||
|
||||
#region Discord Hook
|
||||
|
||||
private void OnRoundEndMessageEvent(RoundEndMessageEvent ev)
|
||||
{
|
||||
if (_webhookSendDuringRound)
|
||||
return;
|
||||
|
||||
var query = EntityManager.EntityQueryEnumerator<StationNewsComponent>();
|
||||
|
||||
while (query.MoveNext(out _, out var comp))
|
||||
{
|
||||
SendArticlesListToDiscordWebhook(comp.Articles.OrderBy(article => article.ShareTime));
|
||||
}
|
||||
}
|
||||
|
||||
private async void SendArticlesListToDiscordWebhook(IOrderedEnumerable<NewsArticle> articles)
|
||||
{
|
||||
foreach (var article in articles)
|
||||
{
|
||||
await Task.Delay(TimeSpan.FromSeconds(1)); // TODO: proper discord rate limit handling
|
||||
await SendArticleToDiscordWebhook(article);
|
||||
}
|
||||
}
|
||||
|
||||
private async Task SendArticleToDiscordWebhook(NewsArticle article)
|
||||
{
|
||||
if (_webhookId is null)
|
||||
return;
|
||||
|
||||
try
|
||||
{
|
||||
var embed = new WebhookEmbed
|
||||
{
|
||||
Title = article.Title,
|
||||
// There is no need to cut article content. It's MaxContentLength smaller then discord's limit (4096):
|
||||
Description = FormattedMessage.RemoveMarkupPermissive(article.Content),
|
||||
Color = _webhookEmbedColor.ToArgb() & 0xFFFFFF, // HACK: way to get hex without A (transparency)
|
||||
Footer = new WebhookEmbedFooter
|
||||
{
|
||||
Text = Loc.GetString("news-discord-footer",
|
||||
("server", _baseServer.ServerName),
|
||||
("round", _ticker.RoundId),
|
||||
("author", article.Author ?? Loc.GetString("news-discord-unknown-author")),
|
||||
("time", article.ShareTime.ToString(@"hh\:mm\:ss")))
|
||||
}
|
||||
};
|
||||
var payload = new WebhookPayload { Embeds = [embed] };
|
||||
await _discord.CreateMessage(_webhookId.Value, payload);
|
||||
Log.Info("Sent news article to Discord webhook");
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
Log.Error($"Error while sending discord news article:\n{e}");
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
using Robust.Shared.Configuration;
|
||||
using Robust.Shared.Maths;
|
||||
|
||||
namespace Content.Shared.CCVar;
|
||||
|
||||
@@ -72,4 +73,24 @@ public sealed partial class CCVars
|
||||
/// </summary>
|
||||
public static readonly CVarDef<float> DiscordWatchlistConnectionBufferTime =
|
||||
CVarDef.Create("discord.watchlist_connection_buffer_time", 5f, CVar.SERVERONLY);
|
||||
|
||||
/// <summary>
|
||||
/// URL of the Discord webhook which will receive station news acticles at the round end.
|
||||
/// If left empty, disables the webhook.
|
||||
/// </summary>
|
||||
public static readonly CVarDef<string> DiscordNewsWebhook =
|
||||
CVarDef.Create("discord.news_webhook", string.Empty, CVar.SERVERONLY);
|
||||
|
||||
/// <summary>
|
||||
/// HEX color of station news discord webhook's embed.
|
||||
/// </summary>
|
||||
public static readonly CVarDef<string> DiscordNewsWebhookEmbedColor =
|
||||
CVarDef.Create("discord.news_webhook_embed_color", Color.LawnGreen.ToHex(), CVar.SERVERONLY);
|
||||
|
||||
/// <summary>
|
||||
/// Whether or not articles should be sent mid-round instead of all at once at the round's end
|
||||
/// </summary>
|
||||
public static readonly CVarDef<bool> DiscordNewsWebhookSendDuringRound =
|
||||
CVarDef.Create("discord.news_webhook_send_during_round", false, CVar.SERVERONLY);
|
||||
|
||||
}
|
||||
|
||||
2
Resources/Locale/en-US/mass-media/news-discord.ftl
Normal file
2
Resources/Locale/en-US/mass-media/news-discord.ftl
Normal file
@@ -0,0 +1,2 @@
|
||||
news-discord-footer = Server: {$server} | Round: #{$round} | Author: {$author} | Time: {$time}
|
||||
news-discord-unknown-author = Unknown
|
||||
Reference in New Issue
Block a user