Skip to content

Commit

Permalink
Handle discord webhook logic
Browse files Browse the repository at this point in the history
  • Loading branch information
Scarpenter1 committed Feb 5, 2025
1 parent 3222bf6 commit 0b7d832
Show file tree
Hide file tree
Showing 3 changed files with 139 additions and 0 deletions.
133 changes: 133 additions & 0 deletions Content.Server/Discord/WebhookMessages/NewsWebhooks.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,133 @@
using Content.Server.Discord;
using Content.Shared.MassMedia.Systems;
using Robust.Shared.Configuration;
using Robust.Shared.GameObjects;
using System.Threading.Tasks;
using Content.Shared.CCVar;
using Content.Server.CartridgeLoader.Cartridges;
using System.Text.RegularExpressions;

namespace Content.Server.Discord.WebhookMessages;

public sealed class NewsWebhooks : EntitySystem
{
[Dependency] private readonly IConfigurationManager _cfg = default!;
[Dependency] private readonly DiscordWebhook _discord = default!;
[Dependency] private readonly ILogManager _log = default!;

private ISawmill _sawmill = default!;

public override void Initialize()
{
base.Initialize();
_sawmill = _log.GetSawmill("discord-news");

_sawmill.Info("[NewsWebhooks] Initialized and ready to receive articles.");
}

/// <summary>
/// Handles a published news article and sends it to Discord.
/// This is called directly from NewsSystem.cs.
/// </summary>
public void HandleNewsPublished(NewsArticle article)
{
_sawmill.Info($"[NewsWebhooks] Directly received article: {article.Title} by {article.Author}");

SendNewsToDiscord(article);
}

private bool TryParseWebhookUrl(string url, out string id, out string token)
{
id = string.Empty;
token = string.Empty;

// Discord webhook format: https://discord.com/api/webhooks/{id}/{token}
var parts = url.Split('/');
if (parts.Length < 7) // Ensure the URL has enough segments
return false;

id = parts[5]; // The 6th element is the webhook ID
token = parts[6]; // The 7th element is the webhook token
return true;
}

private async Task SendNewsToDiscord(NewsArticle article)
{
// Fetch the webhook URL from config
var webhookUrl = _cfg.GetCVar(CCVars.DiscordNewsWebhook);
if (string.IsNullOrWhiteSpace(webhookUrl))
{
_sawmill.Warning("Discord webhook URL is not configured. Skipping news posting.");
return;
}

// Extract ID and Token
if (!TryParseWebhookUrl(webhookUrl, out var webhookId, out var webhookToken))
{
_sawmill.Error($"Invalid Discord webhook URL format: {webhookUrl}");
return;
}

_sawmill.Info($"[NewsWebhooks] Attempting to send news to Discord: {article.Title}");

// Format content using SS14ToDiscordFormatter
string formattedContent = SS14ToDiscordFormatter.ConvertToDiscordMarkup(article.Content);

// Construct payload
var payload = new WebhookPayload
{
Username = "NewsBot",
Embeds = new List<WebhookEmbed>
{
new()
{
Title = article.Title,
Description = formattedContent,
Color = 3447003, // Discord blue
Footer = new WebhookEmbedFooter
{
Text = $"Written by: {article.Author ?? "Unknown"}"
}
}
}
};

// Send webhook with the correct identifier
var response = await _discord.CreateMessage(new WebhookIdentifier(webhookId, webhookToken), payload);

if (response.IsSuccessStatusCode)
{
_sawmill.Info($"[NewsWebhooks] Successfully sent news to Discord: {article.Title}");
}
else
{
var errorMessage = await response.Content.ReadAsStringAsync();
_sawmill.Error($"[NewsWebhooks] Failed to send news to Discord. Status Code: {response.StatusCode}, Response: {errorMessage}");
}
}
}

/// <summary>
/// Converts SS14 news markup to Discord-compatible markup.
/// </summary>
public static class SS14ToDiscordFormatter
{
public static string ConvertToDiscordMarkup(string body)
{
if (string.IsNullOrEmpty(body))
return string.Empty;

// Convert headings (maps [head=1-3] to bold, since Discord has no true headings)
body = Regex.Replace(body, @"\[head=\d\](.*?)\[/head\]", "**$1**");

// Convert color tags (Discord does not support colored text, so remove them)
body = Regex.Replace(body, @"\[color=[^\]]+\](.*?)\[/color\]", "$1");

// Convert italic, bold, and bullet point tags
body = Regex.Replace(body, @"\[italic\](.*?)\[/italic\]", "*$1*");
body = Regex.Replace(body, @"\[bold\](.*?)\[/bold\]", "**$1**");
body = Regex.Replace(body, @"\[bullet\](.*?)\[/bullet\]", "- $1");

return body;
}
}
4 changes: 4 additions & 0 deletions Content.Server/MassMedia/Systems/NewsSystem.cs
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@
using Content.Shared.IdentityManagement;
using Robust.Shared.Timing;
using Content.Shared.GameTicking; // Frontier
using Content.Server.Discord.WebhookMessages;

namespace Content.Server.MassMedia.Systems;

Expand All @@ -35,6 +36,8 @@ 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 NewsWebhooks _newsWebhooks = default!;


public override void Initialize()
{
Expand Down Expand Up @@ -197,6 +200,7 @@ private void OnWriteUiPublishMessage(Entity<NewsWriterComponent> ent, ref NewsWr
}

UpdateWriterDevices();
_newsWebhooks.HandleNewsPublished(article);
}
#endregion

Expand Down
2 changes: 2 additions & 0 deletions Content.Shared/CCVar/CCVars.Discord.cs
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,8 @@ public sealed partial class CCVars
public static readonly CVarDef<string> DiscordVoteWebhook =
CVarDef.Create("discord.vote_webhook", string.Empty, CVar.SERVERONLY);

public static readonly CVarDef<string> DiscordNewsWebhook =
CVarDef.Create("discord.news_webhook", string.Empty, CVar.SERVERONLY);
/// <summary>
/// URL of the Discord webhook which will relay all votekick votes. If left empty, disables the webhook.
/// </summary>
Expand Down

0 comments on commit 0b7d832

Please sign in to comment.