Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
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
6 changes: 3 additions & 3 deletions IW4MAdmin.sln
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Login", "Plugins\Login\Logi
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "ScriptPlugins", "ScriptPlugins", "{3F9ACC27-26DB-49FA-BCD2-50C54A49C9FA}"
ProjectSection(SolutionItems) = preProject
Plugins\ScriptPlugins\ActionOnReport.js = Plugins\ScriptPlugins\ActionOnReport.js
Plugins\ScriptPlugins\ActionOnReport.cs = Plugins\ScriptPlugins\ActionOnReport.cs
Plugins\ScriptPlugins\ParserCoD4x.js = Plugins\ScriptPlugins\ParserCoD4x.js
Plugins\ScriptPlugins\ParserIW4x.js = Plugins\ScriptPlugins\ParserIW4x.js
Plugins\ScriptPlugins\ParserIW6x.js = Plugins\ScriptPlugins\ParserIW6x.js
Expand All @@ -47,8 +47,8 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "ScriptPlugins", "ScriptPlug
Plugins\ScriptPlugins\ParserCSGOSM.js = Plugins\ScriptPlugins\ParserCSGOSM.js
Plugins\ScriptPlugins\ParserPlutoniumT4COZM.js = Plugins\ScriptPlugins\ParserPlutoniumT4COZM.js
Plugins\ScriptPlugins\GameInterface.js = Plugins\ScriptPlugins\GameInterface.js
Plugins\ScriptPlugins\SubnetBan.js = Plugins\ScriptPlugins\SubnetBan.js
Plugins\ScriptPlugins\BanBroadcasting.js = Plugins\ScriptPlugins\BanBroadcasting.js
Plugins\ScriptPlugins\SubnetBan.cs = Plugins\ScriptPlugins\SubnetBan.cs
Plugins\ScriptPlugins\BanBroadcasting.cs = Plugins\ScriptPlugins\BanBroadcasting.cs
Plugins\ScriptPlugins\ParserH1MOD.js = Plugins\ScriptPlugins\ParserH1MOD.js
Plugins\ScriptPlugins\ParserPlutoniumT5.js = Plugins\ScriptPlugins\ParserPlutoniumT5.js
Plugins\ScriptPlugins\ServerBanner.js = Plugins\ScriptPlugins\ServerBanner.js
Expand Down
135 changes: 135 additions & 0 deletions Plugins/ScriptPlugins/ActionOnReport.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,135 @@
#:package RaidMax.IW4MAdmin.SharedLibraryCore@2026.1.6.1

using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using Data.Models.Client;
using Microsoft.Extensions.Logging;
using SharedLibraryCore;
using SharedLibraryCore.Events.Management;
using SharedLibraryCore.Interfaces;
using SharedLibraryCore.Interfaces.Events;
using Microsoft.Extensions.DependencyInjection;

/// <summary>
/// Action on Report Plugin - Automatically bans or temporarily bans players
/// after they receive a certain number of reports.
/// </summary>
public class ActionOnReportPlugin : IPluginV2
{
public static void RegisterDependencies(IServiceCollection serviceCollection)
{
serviceCollection.AddConfiguration<ActionOnReportConfig>(
"ActionOnReportSettings",
new ActionOnReportConfig());
}

public string Name => "Action on Report";
public string Author => "RaidMax";
public string Version => "2.1";

private readonly ILogger<ActionOnReportPlugin> _logger;
private readonly ActionOnReportConfig _config;
private readonly ITranslationLookup _translationLookup;
private readonly Dictionary<long, int> _reportCounts = new();

public ActionOnReportPlugin(
ILogger<ActionOnReportPlugin> logger,
ActionOnReportConfig config,
ITranslationLookup translationLookup)
{
_logger = logger;
_config = config;
_translationLookup = translationLookup;

// Subscribe to penalty events
IManagementEventSubscriptions.ClientPenaltyAdministered += OnPenalty;

_logger.LogInformation("ActionOnReport {Version} by {Author} loaded. Enabled={Enabled}",
Version, Author, _config.Enabled);
}


private Task OnPenalty(ClientPenaltyEvent penaltyEvent, CancellationToken token)
{
if (!_config.Enabled || penaltyEvent.Penalty.Type != Data.Models.EFPenalty.PenaltyType.Report)
{
return Task.CompletedTask;
}

var client = penaltyEvent.Client;

// Ignore if client is not in-game or is privileged
if (!client.IsIngame || (client.Level != EFClient.Permission.User && client.Level != EFClient.Permission.Flagged))
{
_logger.LogInformation("Ignoring report for client (id) {ClientId} because they are privileged or not in-game",
client.ClientId);
return Task.CompletedTask;
}

// Get and increment report count
if (!_reportCounts.TryGetValue(client.NetworkId, out var reportCount))
{
reportCount = 0;
}
reportCount++;
_reportCounts[client.NetworkId] = reportCount;

if (reportCount >= _config.MaxReportCount)
{
var reason = _translationLookup["PLUGINS_REPORT_ACTION"] ?? "Too many reports";

switch (_config.ReportAction)
{
case "TempBan":
_logger.LogInformation("TempBanning client (id) {ClientId} because they received {ReportCount} reports",
client.ClientId, reportCount);
client.TempBan(reason, TimeSpan.FromMinutes(_config.TempBanDurationMinutes),
client.CurrentServer.AsConsoleClient());
break;

case "Ban":
_logger.LogInformation("Banning client (id) {ClientId} because they received {ReportCount} reports",
client.ClientId, reportCount);
client.Ban(reason, client.CurrentServer.AsConsoleClient(), false);
break;
}
}

return Task.CompletedTask;
}

public void Dispose()
{
IManagementEventSubscriptions.ClientPenaltyAdministered -= OnPenalty;
_logger.LogInformation("ActionOnReport unloaded");
}
}

/// <summary>
/// Configuration class for ActionOnReport plugin.
/// </summary>
public class ActionOnReportConfig
{
/// <summary>
/// Indicates if the plugin is enabled.
/// </summary>
public bool Enabled { get; set; } = false;

/// <summary>
/// Action to take when report threshold is reached. Can be "TempBan" or "Ban".
/// </summary>
public string ReportAction { get; set; } = "TempBan";

/// <summary>
/// How many reports before action is taken.
/// </summary>
public int MaxReportCount { get; set; } = 5;

/// <summary>
/// How long to temporarily ban the player (in minutes).
/// </summary>
public int TempBanDurationMinutes { get; set; } = 60;
}
71 changes: 0 additions & 71 deletions Plugins/ScriptPlugins/ActionOnReport.js

This file was deleted.

118 changes: 118 additions & 0 deletions Plugins/ScriptPlugins/BanBroadcasting.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,118 @@
#:package RaidMax.IW4MAdmin.SharedLibraryCore@2026.1.6.1

using System;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.Extensions.Logging;
using SharedLibraryCore;
using SharedLibraryCore.Events.Management;
using SharedLibraryCore.Interfaces;
using SharedLibraryCore.Interfaces.Events;
using Microsoft.Extensions.DependencyInjection;

/// <summary>
/// Broadcasts ban messages to all servers when a client is banned.
/// </summary>
public class BanBroadcastingPlugin : IPluginV2
{
public static void RegisterDependencies(IServiceCollection serviceCollection)
{
serviceCollection.AddConfiguration<BanBroadcastingConfiguration>(
"BanBroadcastingSettings",
new BanBroadcastingConfiguration());
}

public string Name => "Broadcast Bans";
public string Author => "Amos, RaidMax";
public string Version => "2.1";

private readonly ILogger<BanBroadcastingPlugin> _logger;
private readonly BanBroadcastingConfiguration _config;
private readonly ITranslationLookup _translationLookup;
private readonly IManager _manager;

public BanBroadcastingPlugin(
ILogger<BanBroadcastingPlugin> logger,
BanBroadcastingConfiguration config,
ITranslationLookup translationLookup,
IManager manager)
{
_logger = logger;
_config = config;
_translationLookup = translationLookup;
_manager = manager;

// Subscribe to penalty events
IManagementEventSubscriptions.ClientPenaltyAdministered += OnClientPenalty;

_logger.LogInformation("{Name} {Version} by {Author} loaded. Enabled={Enabled}",
Name, Version, Author, _config.EnableBroadcastBans);
}

private Task OnClientPenalty(ClientPenaltyEvent penaltyEvent, CancellationToken token)
{
// Check if broadcasting is enabled
if (!_config.EnableBroadcastBans || penaltyEvent.Penalty.Type != Data.Models.EFPenalty.PenaltyType.Ban)
{
return Task.CompletedTask;
}

string? automatedPenaltyMessage = null;

// Check if the punisher has any automated offenses
if (penaltyEvent.Penalty.Punisher?.AdministeredPenalties != null)
{
foreach (var penalty in penaltyEvent.Penalty.Punisher.AdministeredPenalties)
{
automatedPenaltyMessage = penalty.AutomatedOffense;
break; // Just get the first one if any
}
}

string message;

// Check if the ban was automated (punisher is system client ID 1) and has an automated offense message
if (penaltyEvent.Penalty.PunisherId == 1 && !string.IsNullOrEmpty(automatedPenaltyMessage))
{
var template = _translationLookup["PLUGINS_BROADCAST_BAN_ACMESSAGE"];
message = template?.Replace("{{targetClient}}", penaltyEvent.Client.CleanedName)
?? $"^1{penaltyEvent.Client.CleanedName} ^7has been banned by anti-cheat";
}
else
{
var template = _translationLookup["PLUGINS_BROADCAST_BAN_MESSAGE"];
message = template?.Replace("{{targetClient}}", penaltyEvent.Client.CleanedName)
?? $"^1{penaltyEvent.Client.CleanedName} ^7has been banned";
}

BroadcastMessage(message);

return Task.CompletedTask;
}

private void BroadcastMessage(string message)
{
foreach (var server in _manager.GetServers())
{
server.Broadcast(message);
}
}

public void Dispose()
{
IManagementEventSubscriptions.ClientPenaltyAdministered -= OnClientPenalty;
_logger.LogInformation("{Name} unloaded", Name);
}
}

/// <summary>
/// Configuration class for BanBroadcasting plugin.
/// </summary>
public class BanBroadcastingConfiguration
{
/// <summary>
/// Indicates if the plugin is enabled.
/// </summary>
public bool EnableBroadcastBans { get; set; } = false;
}
Loading
Loading