IW4M-Admin/Plugins/Mute/Plugin.cs
2023-04-05 09:54:57 -05:00

323 lines
13 KiB
C#

using IW4MAdmin.Plugins.Mute.Commands;
using Microsoft.Extensions.DependencyInjection;
using SharedLibraryCore;
using SharedLibraryCore.Commands;
using SharedLibraryCore.Database.Models;
using SharedLibraryCore.Events.Game;
using SharedLibraryCore.Events.Management;
using SharedLibraryCore.Events.Server;
using SharedLibraryCore.Helpers;
using SharedLibraryCore.Interfaces;
using SharedLibraryCore.Interfaces.Events;
using JsonSerializer = System.Text.Json.JsonSerializer;
namespace IW4MAdmin.Plugins.Mute;
public class Plugin : IPluginV2
{
public string Name => "Mute";
public string Version => Utilities.GetVersionAsString();
public string Author => "Amos";
public const string MuteKey = "IW4MMute";
public static IManager Manager { get; private set; } = null!;
public static readonly Server.Game[] SupportedGames = {Server.Game.IW4};
private static readonly string[] DisabledCommands = {nameof(PrivateMessageAdminsCommand), "PrivateMessageCommand"};
private readonly IInteractionRegistration _interactionRegistration;
private readonly IRemoteCommandService _remoteCommandService;
private readonly MuteManager _muteManager;
private const string MuteInteraction = "Webfront::Profile::Mute";
public Plugin(IInteractionRegistration interactionRegistration,
IRemoteCommandService remoteCommandService, MuteManager muteManager)
{
_interactionRegistration = interactionRegistration;
_remoteCommandService = remoteCommandService;
_muteManager = muteManager;
IManagementEventSubscriptions.Load += OnLoad;
IManagementEventSubscriptions.Unload += OnUnload;
IManagementEventSubscriptions.ClientStateInitialized += OnClientStateInitialized;
IGameServerEventSubscriptions.ClientDataUpdated += OnClientDataUpdated;
IGameEventSubscriptions.ClientMessaged += OnClientMessaged;
}
public static void RegisterDependencies(IServiceCollection serviceProvider)
{
serviceProvider.AddSingleton<MuteManager>();
}
private Task OnLoad(IManager manager, CancellationToken cancellationToken)
{
Manager = manager;
manager.CommandInterceptors.Add(gameEvent =>
{
if (gameEvent.Extra is not Command command)
{
return true;
}
var muteMeta = Task.Run(() => _muteManager.GetCurrentMuteState(gameEvent.Origin), cancellationToken)
.GetAwaiter().GetResult();
if (muteMeta.MuteState is not MuteState.Muted)
{
return true;
}
return !DisabledCommands.Contains(command.GetType().Name) && !command.IsBroadcast;
});
_interactionRegistration.RegisterInteraction(MuteInteraction, async (targetClientId, game, token) =>
{
if (!targetClientId.HasValue || game.HasValue && !SupportedGames.Contains((Server.Game)game.Value))
{
return null;
}
var clientMuteMetaState =
(await _muteManager.GetCurrentMuteState(new EFClient {ClientId = targetClientId.Value}))
.MuteState;
var server = manager.GetServers().First();
string GetCommandName(Type commandType) =>
manager.Commands.FirstOrDefault(command => command.GetType() == commandType)?.Name ?? "";
return clientMuteMetaState is MuteState.Unmuted or MuteState.Unmuting
? CreateMuteInteraction(targetClientId.Value, server, GetCommandName)
: CreateUnmuteInteraction(targetClientId.Value, server, GetCommandName);
});
return Task.CompletedTask;
}
private Task OnUnload(IManager manager, CancellationToken token)
{
_interactionRegistration.UnregisterInteraction(MuteInteraction);
return Task.CompletedTask;
}
private async Task OnClientDataUpdated(ClientDataUpdateEvent updateEvent, CancellationToken token)
{
if (!updateEvent.Server.ConnectedClients.Any())
{
return;
}
var networkIds = updateEvent.Clients.Select(client => client.NetworkId).ToList();
var ingameClients = updateEvent.Server.ConnectedClients.Where(client => networkIds.Contains(client.NetworkId));
await Task.WhenAll(ingameClients.Select(async client =>
{
var muteMetaUpdate = await _muteManager.GetCurrentMuteState(client);
if (!muteMetaUpdate.CommandExecuted)
{
await MuteManager.PerformGameCommand(client.CurrentServer, client, muteMetaUpdate);
}
if (muteMetaUpdate.MuteState == MuteState.Muted)
{
// Handle unmute if expired.
if (MuteManager.IsExpiredMute(muteMetaUpdate))
{
await _muteManager.Unmute(client.CurrentServer, Utilities.IW4MAdminClient(), client,
Utilities.CurrentLocalization.LocalizationIndex["PLUGINS_MUTE_EXPIRED"]);
client.Tell(
Utilities.CurrentLocalization.LocalizationIndex["PLUGINS_MUTE_TARGET_EXPIRED"]);
}
}
}));
}
private async Task OnClientMessaged(ClientMessageEvent messageEvent, CancellationToken token)
{
var muteMetaSay = await _muteManager.GetCurrentMuteState(messageEvent.Origin);
if (muteMetaSay.MuteState == MuteState.Muted)
{
// Let the client know when their mute expires.
messageEvent.Origin.Tell(Utilities.CurrentLocalization
.LocalizationIndex["PLUGINS_MUTE_REMAINING_TIME"].FormatExt(
muteMetaSay.Expiration is not null
? muteMetaSay.Expiration.Value.HumanizeForCurrentCulture()
: Utilities.CurrentLocalization.LocalizationIndex["PLUGINS_MUTE_NEVER"],
muteMetaSay.Reason));
}
}
private async Task OnClientStateInitialized(ClientStateInitializeEvent state, CancellationToken token)
{
if (!SupportedGames.Contains(state.Client.CurrentServer.GameName))
{
return;
}
var muteMetaJoin = await _muteManager.GetCurrentMuteState(state.Client);
switch (muteMetaJoin)
{
case { MuteState: MuteState.Muted }:
// Let the client know when their mute expires.
state.Client.Tell(Utilities.CurrentLocalization
.LocalizationIndex["PLUGINS_MUTE_REMAINING_TIME"].FormatExt(
muteMetaJoin is { Expiration: not null }
? muteMetaJoin.Expiration.Value.HumanizeForCurrentCulture()
: Utilities.CurrentLocalization.LocalizationIndex["PLUGINS_MUTE_NEVER"],
muteMetaJoin.Reason));
break;
case { MuteState: MuteState.Unmuting }:
// Handle unmute of unmuted players.
await _muteManager.Unmute(state.Client.CurrentServer, Utilities.IW4MAdminClient(), state.Client,
muteMetaJoin.Reason ?? string.Empty);
state.Client.Tell(Utilities.CurrentLocalization
.LocalizationIndex["PLUGINS_MUTE_COMMANDS_UNMUTE_TARGET_UNMUTED"]
.FormatExt(muteMetaJoin.Reason));
break;
}
}
private InteractionData CreateMuteInteraction(int targetClientId, Server server,
Func<Type, string> getCommandNameFunc)
{
var reasonInput = new
{
Name = "Reason",
Label = Utilities.CurrentLocalization.LocalizationIndex["WEBFRONT_ACTION_LABEL_REASON"],
Type = "text",
Values = (Dictionary<string, string>?)null
};
var durationInput = new
{
Name = "Duration",
Label = Utilities.CurrentLocalization.LocalizationIndex["WEBFRONT_ACTION_LABEL_DURATION"],
Type = "select",
Values = (Dictionary<string, string>?)new Dictionary<string, string>
{
{"5m", TimeSpan.FromMinutes(5).HumanizeForCurrentCulture()},
{"30m", TimeSpan.FromMinutes(30).HumanizeForCurrentCulture()},
{"1h", TimeSpan.FromHours(1).HumanizeForCurrentCulture()},
{"6h", TimeSpan.FromHours(6).HumanizeForCurrentCulture()},
{"1d", TimeSpan.FromDays(1).HumanizeForCurrentCulture()},
{"p", Utilities.CurrentLocalization.LocalizationIndex["WEBFRONT_ACTION_SELECTION_PERMANENT"]}
}
};
var inputs = new[] {reasonInput, durationInput};
var inputsJson = JsonSerializer.Serialize(inputs);
return new InteractionData
{
EntityId = targetClientId,
Name = Utilities.CurrentLocalization.LocalizationIndex["WEBFRONT_PROFILE_CONTEXT_MENU_ACTION_MUTE"],
DisplayMeta = "oi-volume-off",
ActionPath = "DynamicAction",
ActionMeta = new()
{
{"InteractionId", MuteInteraction},
{"Inputs", inputsJson},
{
"ActionButtonLabel",
Utilities.CurrentLocalization.LocalizationIndex["WEBFRONT_PROFILE_CONTEXT_MENU_ACTION_MUTE"]
},
{
"Name",
Utilities.CurrentLocalization.LocalizationIndex["WEBFRONT_PROFILE_CONTEXT_MENU_ACTION_MUTE"]
},
{"ShouldRefresh", true.ToString()}
},
MinimumPermission = Data.Models.Client.EFClient.Permission.Moderator,
Source = Name,
Action = async (originId, targetId, gameName, meta, cancellationToken) =>
{
if (!targetId.HasValue)
{
return "No target client id specified";
}
var isTempMute = meta.ContainsKey(durationInput.Name) &&
meta[durationInput.Name] != durationInput.Values?.Last().Key;
var muteCommand = getCommandNameFunc(isTempMute ? typeof(TempMuteCommand) : typeof(MuteCommand));
var args = new List<string>();
if (meta.TryGetValue(durationInput.Name, out var duration) &&
duration != durationInput.Values?.Last().Key)
{
args.Add(duration);
}
if (meta.TryGetValue(reasonInput.Name, out var reason))
{
args.Add(reason);
}
var commandResponse =
await _remoteCommandService.Execute(originId, targetId, muteCommand, args, server);
return string.Join(".", commandResponse.Select(result => result.Response));
}
};
}
private InteractionData CreateUnmuteInteraction(int targetClientId, Server server,
Func<Type, string> getCommandNameFunc)
{
var reasonInput = new
{
Name = "Reason",
Label = Utilities.CurrentLocalization.LocalizationIndex["WEBFRONT_ACTION_LABEL_REASON"],
Type = "text",
};
var inputs = new[] {reasonInput};
var inputsJson = JsonSerializer.Serialize(inputs);
return new InteractionData
{
EntityId = targetClientId,
Name = Utilities.CurrentLocalization.LocalizationIndex[
"WEBFRONT_PROFILE_CONTEXT_MENU_ACTION_UNMUTE"],
DisplayMeta = "oi-volume-high",
ActionPath = "DynamicAction",
ActionMeta = new()
{
{"InteractionId", MuteInteraction},
{"Outputs", reasonInput.Name},
{"Inputs", inputsJson},
{
"ActionButtonLabel",
Utilities.CurrentLocalization.LocalizationIndex[
"WEBFRONT_PROFILE_CONTEXT_MENU_ACTION_UNMUTE"]
},
{
"Name",
Utilities.CurrentLocalization.LocalizationIndex[
"WEBFRONT_PROFILE_CONTEXT_MENU_ACTION_UNMUTE"]
},
{"ShouldRefresh", true.ToString()}
},
MinimumPermission = Data.Models.Client.EFClient.Permission.Moderator,
Source = Name,
Action = async (originId, targetId, gameName, meta, cancellationToken) =>
{
if (!targetId.HasValue)
{
return "No target client id specified";
}
var args = new List<string>();
if (meta.TryGetValue(reasonInput.Name, out var reason))
{
args.Add(reason);
}
var commandResponse =
await _remoteCommandService.Execute(originId, targetId, getCommandNameFunc(typeof(UnmuteCommand)),
args, server);
return string.Join(".", commandResponse.Select(result => result.Response));
}
};
}
}