Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Last Message Before Death Webhook. #30235

Open
wants to merge 25 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 3 commits
Commits
Show all changes
25 commits
Select commit Hold shift + click to select a range
2c3852e
Added a new webhook to track player's last IC message.
VelonacepsCalyxEggs Jul 21, 2024
1817c59
Code cleanup.
VelonacepsCalyxEggs Jul 21, 2024
18f7be4
Bugfixes and Improvements
VelonacepsCalyxEggs Jul 22, 2024
1f1fce8
Update Content.Server/GameTicking/GameTicker.CVars.cs
VelonacepsCalyxEggs Jul 22, 2024
bbba697
Improvements
VelonacepsCalyxEggs Jul 22, 2024
73b607f
Merge branch 'master' of https://github.com/space-wizards/space-stati…
VelonacepsCalyxEggs Jul 22, 2024
96aeb27
Merge branch 'deathwebhook' of https://github.com/VelonacepsCalyxEggs…
VelonacepsCalyxEggs Jul 22, 2024
fcc8a9c
Use GameTicker instead of IGameTiming.
VelonacepsCalyxEggs Jul 22, 2024
88bdb6a
Convention check
VelonacepsCalyxEggs Jul 26, 2024
6658722
Remove message on ban.
VelonacepsCalyxEggs Jul 28, 2024
d59f327
Merge branch 'master' of https://github.com/space-wizards/space-stati…
VelonacepsCalyxEggs Jul 28, 2024
760dc71
Merge branch 'deathwebhook' of https://github.com/VelonacepsCalyxEggs…
VelonacepsCalyxEggs Jul 28, 2024
749d0cb
Refactor
VelonacepsCalyxEggs Aug 5, 2024
6af4ca1
Code cleanup
VelonacepsCalyxEggs Aug 5, 2024
53edf71
Merge branch 'master' of https://github.com/space-wizards/space-stati…
VelonacepsCalyxEggs Aug 5, 2024
00da285
Merge branch 'space-wizards:master' into deathwebhook
VelonacepsCalyxEggs Aug 5, 2024
faadbdf
Merge branch 'deathwebhook' of https://github.com/VelonacepsCalyxEggs…
VelonacepsCalyxEggs Aug 5, 2024
c55f636
Made webhook persistent.
VelonacepsCalyxEggs Aug 10, 2024
e601912
Code refactor (big)
VelonacepsCalyxEggs Aug 29, 2024
1120c61
Fix merge conflict.
VelonacepsCalyxEggs Oct 29, 2024
fabac59
Fix a foolish mistake...
VelonacepsCalyxEggs Oct 29, 2024
53f2c99
Merge remote-tracking branch 'upstream/master' into deathwebhook
VelonacepsCalyxEggs Nov 25, 2024
00ff25c
Merge branch 'space-wizards:master' into deathwebhook
VelonacepsCalyxEggs Dec 15, 2024
1ef259a
Now keying the user by NetUserId and their character by Mind.
VelonacepsCalyxEggs Dec 15, 2024
57181e4
Merge branch 'master' into deathwebhook
VelonacepsCalyxEggs Jan 18, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
142 changes: 142 additions & 0 deletions Content.Server/Chat/LastMessageBeforeDeath.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,142 @@
using System.Text;
using Content.Server.Discord;
using Robust.Shared.Timing;
using Content.Shared.Mobs.Systems;
using System.Threading.Tasks;
using System.Collections.Specialized;
using System.Collections;

namespace Content.Server.Chat
{
internal class LastMessageBeforeDeath
{
[Dependency] private readonly IGameTiming _gameTiming = default!;
#if DEBUG
[Dependency] private readonly ILogManager _logManager = default!;
private ISawmill _sawmill = default!;
#endif
private readonly MobStateSystem _mobStateSystem;

private OrderedDictionary _playerData = new OrderedDictionary();

private static LastMessageBeforeDeath? _instance;
private static readonly object _lock = new object();

// Private constructor to prevent instantiation
private LastMessageBeforeDeath()
{
IoCManager.InjectDependencies(this);
_mobStateSystem = IoCManager.Resolve<IEntityManager>().System<MobStateSystem>();
#if DEBUG
_sawmill = _logManager.GetSawmill("lastDeathMsgWebhook");
#endif
}

// Public property to get the singleton instance
public static LastMessageBeforeDeath Instance
{
get
{
lock (_lock)
{
if (_instance == null)
{
_instance = new LastMessageBeforeDeath();
}
return _instance;
}
}
}

// Method to add a message for a player
public async void AddMessage(string playerName, EntityUid player, string message)
{
if (_gameTiming == null)
{
throw new InvalidOperationException("GameTiming is not initialized.");
}

var currentTime = _gameTiming.CurTime;
string truncatedTime = $"{currentTime.Hours:D2}:{currentTime.Minutes:D2}:{currentTime.Seconds:D2}";
string formattedMessage = $"[{truncatedTime}] {playerName}: {message}";

if (!_playerData.Contains(player))
{
_playerData[player] = new OrderedDictionary();
}

var playerMessages = _playerData[player] as OrderedDictionary ?? throw new InvalidOperationException("Player messages dictionary is not initialized.");
playerMessages[playerName] = formattedMessage;
}

// Send messages through the webhook on round end
public async void OnRoundEnd(WebhookIdentifier? webhookId)
{
if (!webhookId.HasValue)
return;

var id = webhookId.Value;
StringBuilder allMessagesString = new StringBuilder();
foreach (DictionaryEntry player in _playerData)
{
var playerUid = (EntityUid)player.Key;
if (_mobStateSystem.IsDead(playerUid))
{
var playerMessages = player.Value as OrderedDictionary;
if (playerMessages != null)
{
foreach (DictionaryEntry playerName in playerMessages)
{
allMessagesString.AppendLine($"{playerName.Value}");
}
}
}
}

if (allMessagesString.ToString().Length == 0)
{
allMessagesString.AppendLine($"No messages found.");
}

var messages = SplitMessage(allMessagesString.ToString(), 2000);
var discordWebhook = new DiscordWebhook();
int messageCount = 0;
foreach (var message in messages)
{
if (messageCount >= 30)
{
await Task.Delay(60000); // Wait for 1 minute
messageCount = 0;
}
var payload = new WebhookPayload { Content = message };
var response = await discordWebhook.CreateMessage(id, payload);
messageCount++;

// Response still can be handled if needed.
#if DEBUG
if (response.IsSuccessStatusCode)
{
_sawmill.Debug("Last Messages Before Death sent successfully.");
}
else
{
_sawmill.Error($"Failed to send last messages before death. Status code: {response.StatusCode}");
}
#endif
}
}



private List<string> SplitMessage(string message, int chunkSize)
{
var messages = new List<string>();
for (int i = 0; i < message.Length; i += chunkSize)
{
messages.Add(message.Substring(i, Math.Min(chunkSize, message.Length - i)));
}
return messages;
}

}
}
8 changes: 8 additions & 0 deletions Content.Server/Chat/Systems/ChatSystem.cs
Original file line number Diff line number Diff line change
Expand Up @@ -258,6 +258,14 @@ public void TrySendInGameICMessage(
SendEntityEmote(source, message, range, nameOverride, hideLog: hideLog, ignoreActionBlocker: ignoreActionBlocker);
break;
}

//Last Message Before Death Webhook
var lastMessageSystem = LastMessageBeforeDeath.Instance;

if (player != null)
{
lastMessageSystem.AddMessage(Name(source), source, message);
}
}

public void TrySendInGameOOCMessage(
Expand Down
12 changes: 12 additions & 0 deletions Content.Server/GameTicking/GameTicker.CVars.cs
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,10 @@ public sealed partial class GameTicker

private WebhookIdentifier? _webhookIdentifier;

[ViewVariables]

private WebhookIdentifier? _webhookIdentifierLastMessage;

[ViewVariables]
private string? RoundEndSoundCollection { get; set; }

Expand Down Expand Up @@ -72,6 +76,14 @@ private void InitializeCVars()
DiscordRoundEndRole = null;
}
}, true);
//CCVar for DiscordLastMessageBeforeDeathWebhook
Subs.CVar(_configurationManager, CCVars.DiscordLastMessageBeforeDeathWebhook, value =>
{
if (!string.IsNullOrWhiteSpace(value))
{
_discord.GetWebhook(value, data => _webhookIdentifierLastMessage = data.ToIdentifier());
}
}, true);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This kind of stuff should be done on LastMessageBeforeDeathSystem.

Subs.CVar(_configurationManager, CCVars.RoundEndSoundCollection, value => RoundEndSoundCollection = value, true);
#if EXCEPTION_TOLERANCE
Subs.CVar(_configurationManager, CCVars.RoundStartFailShutdownCount, value => RoundStartFailShutdownCount = value, true);
Expand Down
4 changes: 4 additions & 0 deletions Content.Server/GameTicking/GameTicker.RoundFlow.cs
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
using System.Linq;
using Content.Server.Announcements;
using Content.Server.Chat;
using Content.Server.Discord;
using Content.Server.GameTicking.Events;
using Content.Server.Ghost;
Expand Down Expand Up @@ -341,6 +342,9 @@ public void EndRound(string text = "")

ShowRoundEndScoreboard(text);
SendRoundEndDiscordMessage();
// Call the OnEndRound to send the message through Last Message Before Death Webhook.
var lastMessageSystem = LastMessageBeforeDeath.Instance;
lastMessageSystem.OnRoundEnd(_webhookIdentifierLastMessage);
}

public void ShowRoundEndScoreboard(string text = "")
Expand Down
6 changes: 6 additions & 0 deletions Content.Shared/CCVar/CCVars.cs
Original file line number Diff line number Diff line change
Expand Up @@ -490,6 +490,12 @@ public static readonly CVarDef<bool>
public static readonly CVarDef<string> DiscordRoundEndRoleWebhook =
CVarDef.Create("discord.round_end_role", string.Empty, CVar.SERVERONLY);

/// <summary>
/// URL of the Discord webhook which will relay last messages before death.
/// </summary>
public static readonly CVarDef<string> DiscordLastMessageBeforeDeathWebhook =
CVarDef.Create("discord.last_message_before_death_webhook", string.Empty, CVar.SERVERONLY | CVar.CONFIDENTIAL);

/*
* Tips
*/
Expand Down
Loading