6
StarHorizon_Public/Content.Server/_Horizon/Chat/OocSpamProtectionSystem.cs
2026-01-24 12:49:55 +03:00

188 lines
6.6 KiB
C#

using Robust.Shared.Prototypes;
using Content.Shared.StatusEffect;
using Content.Shared.Chat;
using Content.Shared.Chat.Components;
using Robust.Shared.Timing;
using Robust.Shared.Audio;
using Robust.Shared.Audio.Systems;
using Robust.Shared.Player;
using Content.Server.Chat.Systems;
using Robust.Shared.Localization;
using Content.Shared.Speech;
using Content.Server.Chat.Managers;
using Content.Shared.Players;
using Robust.Shared.Utility;
namespace Content.Server._Horizon.Chat;
[Access(typeof(OocSpamProtectionComponent), typeof(ChatSystem))]
public sealed class OocSpamProtectionSystem : EntitySystem
{
[Dependency] private readonly IChatManager _chatManager = default!;
[Dependency] private readonly ChatSystem _chat = default!;
[Dependency] private readonly StatusEffectsSystem _statusEffects = default!;
[Dependency] private readonly IGameTiming _gameTiming = default!;
[Dependency] private readonly SharedAudioSystem _audio = default!;
[Dependency] private readonly ISharedPlayerManager _playerManager = default!;
// ID эффекта немоты
private const string MuteEffectId = "Muted";
public override void Initialize()
{
base.Initialize();
SubscribeLocalEvent<OocSpamProtectionComponent, ComponentInit>(OnComponentInit);
}
private void OnComponentInit(EntityUid uid, OocSpamProtectionComponent component, ComponentInit args)
{
component.MessageTimestamps = new TimeSpan[component.MaxTrackedMessages];
component.RecentMessages = new string[component.MaxTrackedMessages];
}
// Метод для проверки OOC сообщения (будет вызываться из ChatSystem)
public bool CheckOocSpam(EntityUid uid, string message, OocSpamProtectionComponent comp)
{
// Проверяем, не немой ли уже игрок
if (_statusEffects.HasStatusEffect(uid, MuteEffectId))
{
var session = GetPlayerSession(uid);
if (session != null)
{
_chatManager.DispatchServerMessage(session,
Loc.GetString("spam-protection-muted"));
}
return false; // Блокируем сообщение
}
var currentTime = _gameTiming.CurTime;
// Обновляем историю сообщений
UpdateMessageHistory(message, currentTime, comp);
// Проверяем на спам
if (IsSpamming(comp, currentTime))
{
var session = GetPlayerSession(uid);
// Сохраняем сообщение для логирования перед очисткой
var spamMessage = comp.RecentMessages[0] ?? message;
HandleOocSpam(uid, comp, session);
// Логируем нарушение
LogSpamViolation(uid, spamMessage);
return false; // Блокируем сообщение
}
return true; // Разрешаем сообщение
}
private void UpdateMessageHistory(string message, TimeSpan currentTime, OocSpamProtectionComponent comp)
{
// Сдвигаем массив на одну позицию
for (int i = comp.MaxTrackedMessages - 1; i > 0; i--)
{
comp.RecentMessages[i] = comp.RecentMessages[i - 1];
comp.MessageTimestamps[i] = comp.MessageTimestamps[i - 1];
}
// Добавляем новое сообщение
comp.RecentMessages[0] = message;
comp.MessageTimestamps[0] = currentTime;
comp.MessageCount = Math.Min(comp.MessageCount + 1, comp.MaxTrackedMessages);
}
private bool IsSpamming(OocSpamProtectionComponent comp, TimeSpan currentTime)
{
if (comp.MessageCount < comp.SpamTriggerCount)
return false;
// Проверяем, что все N сообщений одинаковые
string firstMessage = comp.RecentMessages[0];
if (string.IsNullOrEmpty(firstMessage))
return false;
for (int i = 1; i < comp.SpamTriggerCount; i++)
{
if (comp.RecentMessages[i] != firstMessage)
return false;
}
// Проверяем временной интервал
var oldestIndex = comp.SpamTriggerCount - 1;
var timeDiff = currentTime - comp.MessageTimestamps[oldestIndex];
return timeDiff.TotalSeconds <= comp.SpamTimeThreshold;
}
private void HandleOocSpam(EntityUid uid, OocSpamProtectionComponent comp, ICommonSession? session)
{
// 1. Применяем эффект немоты
ApplyMuteEffect(uid, comp, session);
// 2. Очищаем историю сообщений
ClearMessageHistory(comp);
}
private void ApplyMuteEffect(EntityUid uid, OocSpamProtectionComponent comp, ICommonSession? session)
{
// Применяем статус-эффект
_statusEffects.TryAddStatusEffect(uid, MuteEffectId,
TimeSpan.FromSeconds(comp.MuteDuration),
refresh: false);
// Уведомление игроку
if (session != null)
{
var message = Loc.GetString("spam-protection-you-choked-ooc",
("duration", comp.MuteDuration));
_chatManager.DispatchServerMessage(session, message);
}
}
private void ClearMessageHistory(OocSpamProtectionComponent comp)
{
Array.Clear(comp.RecentMessages, 0, comp.RecentMessages.Length);
Array.Clear(comp.MessageTimestamps, 0, comp.MessageTimestamps.Length);
comp.MessageCount = 0;
}
private ICommonSession? GetPlayerSession(EntityUid uid)
{
if (TryComp<ActorComponent>(uid, out var actor))
return actor.PlayerSession;
foreach (var session in _playerManager.Sessions)
{
if (session.AttachedEntity == uid)
return session;
}
return null;
}
private void LogSpamViolation(EntityUid uid, string message)
{
// Безопасное получение имени entity
string playerName;
if (TryComp<MetaDataComponent>(uid, out var meta))
{
playerName = meta.EntityName;
}
else
{
playerName = uid.ToString();
}
// Безопасная обработка сообщения
var shortMessage = string.IsNullOrEmpty(message)
? "[пустое сообщение]"
: (message.Length > 50 ? message[..47] + "..." : message);
Logger.Info($"{playerName} получил мут в OOC за спам: \"{shortMessage}\"");
}
}