2023-02-11 21:48:31 -05:00
|
|
|
|
using IW4MAdmin.Plugins.Mute.Commands;
|
|
|
|
|
using Microsoft.Extensions.DependencyInjection;
|
2022-10-05 16:29:31 -04:00
|
|
|
|
using SharedLibraryCore;
|
|
|
|
|
using SharedLibraryCore.Commands;
|
2022-09-08 16:03:38 -04:00
|
|
|
|
using SharedLibraryCore.Database.Models;
|
2023-02-11 21:48:31 -05:00
|
|
|
|
using SharedLibraryCore.Events.Game;
|
|
|
|
|
using SharedLibraryCore.Events.Management;
|
|
|
|
|
using SharedLibraryCore.Events.Server;
|
2022-09-08 16:03:38 -04:00
|
|
|
|
using SharedLibraryCore.Helpers;
|
2022-08-26 13:09:33 -04:00
|
|
|
|
using SharedLibraryCore.Interfaces;
|
2023-02-11 21:48:31 -05:00
|
|
|
|
using SharedLibraryCore.Interfaces.Events;
|
2022-10-05 16:29:31 -04:00
|
|
|
|
using JsonSerializer = System.Text.Json.JsonSerializer;
|
2022-08-26 13:09:33 -04:00
|
|
|
|
|
2023-02-11 21:48:31 -05:00
|
|
|
|
namespace IW4MAdmin.Plugins.Mute;
|
2022-08-26 13:09:33 -04:00
|
|
|
|
|
2023-02-11 21:48:31 -05:00
|
|
|
|
public class Plugin : IPluginV2
|
2022-08-26 13:09:33 -04:00
|
|
|
|
{
|
2022-10-05 16:29:31 -04:00
|
|
|
|
public string Name => "Mute";
|
2023-02-11 21:48:31 -05:00
|
|
|
|
public string Version => Utilities.GetVersionAsString();
|
2022-10-05 16:29:31 -04:00
|
|
|
|
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"};
|
2022-09-08 16:03:38 -04:00
|
|
|
|
private readonly IInteractionRegistration _interactionRegistration;
|
2022-10-13 11:02:14 -04:00
|
|
|
|
private readonly IRemoteCommandService _remoteCommandService;
|
2023-02-11 21:48:31 -05:00
|
|
|
|
private readonly MuteManager _muteManager;
|
|
|
|
|
private const string MuteInteraction = "Webfront::Profile::Mute";
|
2022-09-08 16:03:38 -04:00
|
|
|
|
|
2023-02-11 21:48:31 -05:00
|
|
|
|
public Plugin(IInteractionRegistration interactionRegistration,
|
|
|
|
|
IRemoteCommandService remoteCommandService, MuteManager muteManager)
|
2022-08-26 13:09:33 -04:00
|
|
|
|
{
|
2022-09-08 16:03:38 -04:00
|
|
|
|
_interactionRegistration = interactionRegistration;
|
2022-10-13 11:02:14 -04:00
|
|
|
|
_remoteCommandService = remoteCommandService;
|
2023-02-11 21:48:31 -05:00
|
|
|
|
_muteManager = muteManager;
|
|
|
|
|
|
|
|
|
|
IManagementEventSubscriptions.Load += OnLoad;
|
|
|
|
|
IManagementEventSubscriptions.Unload += OnUnload;
|
|
|
|
|
|
|
|
|
|
IManagementEventSubscriptions.ClientStateInitialized += OnClientStateInitialized;
|
|
|
|
|
IGameServerEventSubscriptions.ClientDataUpdated += OnClientDataUpdated;
|
|
|
|
|
IGameEventSubscriptions.ClientMessaged += OnClientMessaged;
|
2022-08-26 13:09:33 -04:00
|
|
|
|
}
|
|
|
|
|
|
2023-02-11 21:48:31 -05:00
|
|
|
|
public static void RegisterDependencies(IServiceCollection serviceProvider)
|
2022-08-26 13:09:33 -04:00
|
|
|
|
{
|
2023-02-11 21:48:31 -05:00
|
|
|
|
serviceProvider.AddSingleton<MuteManager>();
|
2022-08-26 13:09:33 -04:00
|
|
|
|
}
|
|
|
|
|
|
2023-02-11 21:48:31 -05:00
|
|
|
|
private Task OnLoad(IManager manager, CancellationToken cancellationToken)
|
2022-08-26 13:09:33 -04:00
|
|
|
|
{
|
2022-10-05 16:29:31 -04:00
|
|
|
|
Manager = manager;
|
|
|
|
|
|
|
|
|
|
manager.CommandInterceptors.Add(gameEvent =>
|
|
|
|
|
{
|
2022-10-13 11:02:14 -04:00
|
|
|
|
if (gameEvent.Extra is not Command command)
|
2022-10-14 09:47:01 -04:00
|
|
|
|
{
|
2022-10-13 11:02:14 -04:00
|
|
|
|
return true;
|
2022-10-14 09:47:01 -04:00
|
|
|
|
}
|
|
|
|
|
|
2023-04-05 10:54:57 -04:00
|
|
|
|
var muteMeta = Task.Run(() => _muteManager.GetCurrentMuteState(gameEvent.Origin), cancellationToken)
|
|
|
|
|
.GetAwaiter().GetResult();
|
|
|
|
|
|
2022-10-14 09:47:01 -04:00
|
|
|
|
if (muteMeta.MuteState is not MuteState.Muted)
|
|
|
|
|
{
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
2022-10-05 16:29:31 -04:00
|
|
|
|
return !DisabledCommands.Contains(command.GetType().Name) && !command.IsBroadcast;
|
|
|
|
|
});
|
|
|
|
|
|
2022-10-13 11:02:14 -04:00
|
|
|
|
_interactionRegistration.RegisterInteraction(MuteInteraction, async (targetClientId, game, token) =>
|
2022-09-08 16:03:38 -04:00
|
|
|
|
{
|
2022-10-24 19:58:12 -04:00
|
|
|
|
if (!targetClientId.HasValue || game.HasValue && !SupportedGames.Contains((Server.Game)game.Value))
|
2022-09-08 16:03:38 -04:00
|
|
|
|
{
|
|
|
|
|
return null;
|
|
|
|
|
}
|
|
|
|
|
|
2022-10-13 11:02:14 -04:00
|
|
|
|
var clientMuteMetaState =
|
2023-02-11 21:48:31 -05:00
|
|
|
|
(await _muteManager.GetCurrentMuteState(new EFClient {ClientId = targetClientId.Value}))
|
2022-10-05 16:29:31 -04:00
|
|
|
|
.MuteState;
|
2022-10-13 11:02:14 -04:00
|
|
|
|
var server = manager.GetServers().First();
|
|
|
|
|
|
|
|
|
|
string GetCommandName(Type commandType) =>
|
|
|
|
|
manager.Commands.FirstOrDefault(command => command.GetType() == commandType)?.Name ?? "";
|
2022-10-05 16:29:31 -04:00
|
|
|
|
|
|
|
|
|
return clientMuteMetaState is MuteState.Unmuted or MuteState.Unmuting
|
2022-10-13 11:02:14 -04:00
|
|
|
|
? CreateMuteInteraction(targetClientId.Value, server, GetCommandName)
|
|
|
|
|
: CreateUnmuteInteraction(targetClientId.Value, server, GetCommandName);
|
|
|
|
|
});
|
|
|
|
|
return Task.CompletedTask;
|
|
|
|
|
}
|
2023-02-11 21:48:31 -05:00
|
|
|
|
|
|
|
|
|
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;
|
|
|
|
|
}
|
|
|
|
|
}
|
2022-10-13 11:02:14 -04:00
|
|
|
|
|
2022-10-13 13:18:06 -04:00
|
|
|
|
private InteractionData CreateMuteInteraction(int targetClientId, Server server,
|
|
|
|
|
Func<Type, string> getCommandNameFunc)
|
2022-10-13 11:02:14 -04:00
|
|
|
|
{
|
|
|
|
|
var reasonInput = new
|
|
|
|
|
{
|
|
|
|
|
Name = "Reason",
|
|
|
|
|
Label = Utilities.CurrentLocalization.LocalizationIndex["WEBFRONT_ACTION_LABEL_REASON"],
|
|
|
|
|
Type = "text",
|
2022-10-24 19:58:12 -04:00
|
|
|
|
Values = (Dictionary<string, string>?)null
|
2022-10-13 11:02:14 -04:00
|
|
|
|
};
|
2022-10-13 13:18:06 -04:00
|
|
|
|
|
2022-10-13 11:02:14 -04:00
|
|
|
|
var durationInput = new
|
|
|
|
|
{
|
|
|
|
|
Name = "Duration",
|
|
|
|
|
Label = Utilities.CurrentLocalization.LocalizationIndex["WEBFRONT_ACTION_LABEL_DURATION"],
|
|
|
|
|
Type = "select",
|
2022-10-24 19:58:12 -04:00
|
|
|
|
Values = (Dictionary<string, string>?)new Dictionary<string, string>
|
2022-10-13 11:02:14 -04:00
|
|
|
|
{
|
2022-10-13 13:18:06 -04:00
|
|
|
|
{"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"]}
|
2022-10-13 11:02:14 -04:00
|
|
|
|
}
|
|
|
|
|
};
|
2022-10-13 13:18:06 -04:00
|
|
|
|
|
|
|
|
|
var inputs = new[] {reasonInput, durationInput};
|
2022-10-13 11:02:14 -04:00
|
|
|
|
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()
|
|
|
|
|
{
|
2022-10-13 13:18:06 -04:00
|
|
|
|
{"InteractionId", MuteInteraction},
|
|
|
|
|
{"Inputs", inputsJson},
|
2022-09-08 16:03:38 -04:00
|
|
|
|
{
|
2022-10-13 11:02:14 -04:00
|
|
|
|
"ActionButtonLabel",
|
|
|
|
|
Utilities.CurrentLocalization.LocalizationIndex["WEBFRONT_PROFILE_CONTEXT_MENU_ACTION_MUTE"]
|
|
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
"Name",
|
|
|
|
|
Utilities.CurrentLocalization.LocalizationIndex["WEBFRONT_PROFILE_CONTEXT_MENU_ACTION_MUTE"]
|
|
|
|
|
},
|
2022-10-13 13:18:06 -04:00
|
|
|
|
{"ShouldRefresh", true.ToString()}
|
2022-10-13 11:02:14 -04:00
|
|
|
|
},
|
|
|
|
|
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";
|
2022-09-08 16:03:38 -04:00
|
|
|
|
}
|
2022-10-13 11:02:14 -04:00
|
|
|
|
|
|
|
|
|
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)
|
2022-09-08 16:03:38 -04:00
|
|
|
|
{
|
2022-10-13 11:02:14 -04:00
|
|
|
|
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));
|
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
}
|
|
|
|
|
|
2022-10-13 13:18:06 -04:00
|
|
|
|
private InteractionData CreateUnmuteInteraction(int targetClientId, Server server,
|
|
|
|
|
Func<Type, string> getCommandNameFunc)
|
2022-10-13 11:02:14 -04:00
|
|
|
|
{
|
|
|
|
|
var reasonInput = new
|
|
|
|
|
{
|
|
|
|
|
Name = "Reason",
|
|
|
|
|
Label = Utilities.CurrentLocalization.LocalizationIndex["WEBFRONT_ACTION_LABEL_REASON"],
|
|
|
|
|
Type = "text",
|
|
|
|
|
};
|
|
|
|
|
|
2022-10-13 13:18:06 -04:00
|
|
|
|
var inputs = new[] {reasonInput};
|
2022-10-13 11:02:14 -04:00
|
|
|
|
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()
|
|
|
|
|
{
|
2022-10-13 13:18:06 -04:00
|
|
|
|
{"InteractionId", MuteInteraction},
|
|
|
|
|
{"Outputs", reasonInput.Name},
|
|
|
|
|
{"Inputs", inputsJson},
|
2022-10-13 11:02:14 -04:00
|
|
|
|
{
|
|
|
|
|
"ActionButtonLabel",
|
|
|
|
|
Utilities.CurrentLocalization.LocalizationIndex[
|
|
|
|
|
"WEBFRONT_PROFILE_CONTEXT_MENU_ACTION_UNMUTE"]
|
|
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
"Name",
|
|
|
|
|
Utilities.CurrentLocalization.LocalizationIndex[
|
|
|
|
|
"WEBFRONT_PROFILE_CONTEXT_MENU_ACTION_UNMUTE"]
|
|
|
|
|
},
|
2022-10-13 13:18:06 -04:00
|
|
|
|
{"ShouldRefresh", true.ToString()}
|
2022-10-13 11:02:14 -04:00
|
|
|
|
},
|
|
|
|
|
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 =
|
2022-10-13 13:18:06 -04:00
|
|
|
|
await _remoteCommandService.Execute(originId, targetId, getCommandNameFunc(typeof(UnmuteCommand)),
|
|
|
|
|
args, server);
|
2022-10-13 11:02:14 -04:00
|
|
|
|
return string.Join(".", commandResponse.Select(result => result.Response));
|
|
|
|
|
}
|
|
|
|
|
};
|
2022-08-26 13:09:33 -04:00
|
|
|
|
}
|
|
|
|
|
}
|