diff --git a/Application/BuildScripts/DownloadTranslations.ps1 b/Application/BuildScripts/DownloadTranslations.ps1 index 0b67d2592..7286ca4b4 100644 --- a/Application/BuildScripts/DownloadTranslations.ps1 +++ b/Application/BuildScripts/DownloadTranslations.ps1 @@ -7,6 +7,6 @@ foreach($localization in $localizations) { $url = "http://api.raidmax.org:5000/localization/{0}" -f $localization $filePath = "{0}Localization\IW4MAdmin.{1}.json" -f $OutputDir, $localization - $response = Invoke-WebRequest $url + $response = Invoke-WebRequest $url -UseBasicParsing Out-File -FilePath $filePath -InputObject $response.Content -Encoding utf8 -} \ No newline at end of file +} diff --git a/Data/Models/EFPenalty.cs b/Data/Models/EFPenalty.cs index 66f296d21..b5abc83ff 100644 --- a/Data/Models/EFPenalty.cs +++ b/Data/Models/EFPenalty.cs @@ -18,6 +18,9 @@ namespace Data.Models Unban, Any, Unflag, + Mute, + TempMute, + Unmute, Other = 100 } diff --git a/DeploymentFiles/PostPublish.ps1 b/DeploymentFiles/PostPublish.ps1 index a80554e7c..0bcc9bbda 100644 --- a/DeploymentFiles/PostPublish.ps1 +++ b/DeploymentFiles/PostPublish.ps1 @@ -9,7 +9,7 @@ foreach($localization in $localizations) { $url = "http://api.raidmax.org:5000/localization/{0}" -f $localization $filePath = "{0}\Localization\IW4MAdmin.{1}.json" -f $PublishDir, $localization - $response = Invoke-WebRequest $url + $response = Invoke-WebRequest $url -UseBasicParsing Out-File -FilePath $filePath -InputObject $response.Content -Encoding utf8 } @@ -20,4 +20,4 @@ Minor = $versionInfo.ProductMinorPart Build = $versionInfo.ProductBuildPart Revision = $versionInfo.ProductPrivatePart } -$json | ConvertTo-Json | Out-File -FilePath ("{0}\VersionInformation.json" -f $PublishDir) -Encoding ASCII \ No newline at end of file +$json | ConvertTo-Json | Out-File -FilePath ("{0}\VersionInformation.json" -f $PublishDir) -Encoding ASCII diff --git a/DeploymentFiles/UpdateIW4MAdmin.ps1 b/DeploymentFiles/UpdateIW4MAdmin.ps1 index a3964412b..b871f255c 100644 --- a/DeploymentFiles/UpdateIW4MAdmin.ps1 +++ b/DeploymentFiles/UpdateIW4MAdmin.ps1 @@ -39,7 +39,7 @@ else Write-Output "Retrieving latest version info..." -$releaseInfo = (Invoke-WebRequest $releasesUri | ConvertFrom-Json) | Select -First 1 +$releaseInfo = (Invoke-WebRequest $releasesUri -UseBasicParsing | ConvertFrom-Json) | Select -First 1 $asset = $releaseInfo.assets | Where-Object name -like $assetPattern | Select -First 1 $downloadUri = $asset.browser_download_url $filename = Split-Path $downloadUri -leaf @@ -55,7 +55,7 @@ if (!$Silent) Write-Output "Downloading update. This might take a moment..." -$fileDownload = Invoke-WebRequest -Uri $downloadUri +$fileDownload = Invoke-WebRequest -Uri $downloadUri -UseBasicParsing if ($fileDownload.StatusDescription -ne "OK") { throw "Could not update IW4MAdmin. ($fileDownload.StatusDescription)" diff --git a/Plugins/Mute/Commands/MuteCommand.cs b/Plugins/Mute/Commands/MuteCommand.cs index 04047317a..fc0c28218 100644 --- a/Plugins/Mute/Commands/MuteCommand.cs +++ b/Plugins/Mute/Commands/MuteCommand.cs @@ -23,20 +23,27 @@ public class MuteCommand : Command { Name = translationLookup["COMMANDS_ARGS_PLAYER"], Required = true + }, + new CommandArgument + { + Name = translationLookup["COMMANDS_ARGS_REASON"], + Required = true } }; } - private readonly MuteManager _muteManager = new(); - public override async Task ExecuteAsync(GameEvent gameEvent) { - if (await _muteManager.Mute(gameEvent)) + if (await Plugin.MuteManager.Mute(gameEvent.Owner, gameEvent.Origin, gameEvent.Target, null, gameEvent.Data)) { - gameEvent.Origin.Tell($"{_translationLookup["PLUGINS_MUTE_MUTED"]} {gameEvent.Target.Name}"); + gameEvent.Origin.Tell(_translationLookup["PLUGINS_MUTE_COMMANDS_MUTE_MUTED"] + .FormatExt(gameEvent.Target.CleanedName)); + gameEvent.Target.Tell(_translationLookup["PLUGINS_MUTE_COMMANDS_MUTE_TARGET_MUTED"] + .FormatExt(gameEvent.Data)); return; } - gameEvent.Origin.Tell($"{_translationLookup["PLUGINS_MUTE_UNMUTED"]} {gameEvent.Target.Name}"); + gameEvent.Origin.Tell(_translationLookup["PLUGINS_MUTE_COMMANDS_MUTE_NOT_UNMUTED"] + .FormatExt(gameEvent.Target.CleanedName)); } } diff --git a/Plugins/Mute/Commands/TempMuteCommand.cs b/Plugins/Mute/Commands/TempMuteCommand.cs new file mode 100644 index 000000000..6914ad77d --- /dev/null +++ b/Plugins/Mute/Commands/TempMuteCommand.cs @@ -0,0 +1,67 @@ +using System.Text.RegularExpressions; +using Data.Models.Client; +using SharedLibraryCore; +using SharedLibraryCore.Commands; +using SharedLibraryCore.Configuration; +using SharedLibraryCore.Interfaces; + +namespace Mute.Commands; + +public class TempMuteCommand : Command +{ + private const string TempBanRegex = @"([0-9]+\w+)\ (.+)"; + + public TempMuteCommand(CommandConfiguration config, ITranslationLookup translationLookup) : base(config, + translationLookup) + { + Name = "tempmute"; + Description = translationLookup["PLUGINS_MUTE_COMMANDS_TEMPMUTE_DESC"]; + Alias = "tm"; + Permission = EFClient.Permission.Moderator; + RequiresTarget = true; + SupportedGames = Plugin.SupportedGames; + Arguments = new[] + { + new CommandArgument + { + Name = translationLookup["COMMANDS_ARGS_PLAYER"], + Required = true + }, + new CommandArgument + { + Name = translationLookup["COMMANDS_ARGS_DURATION"], + Required = true + }, + new CommandArgument + { + Name = translationLookup["COMMANDS_ARGS_REASON"], + Required = true + } + }; + } + + public override async Task ExecuteAsync(GameEvent gameEvent) + { + var match = Regex.Match(gameEvent.Data, TempBanRegex); + if (match.Success) + { + var expiration = DateTime.UtcNow + match.Groups[1].ToString().ParseTimespan(); + var reason = match.Groups[2].ToString(); + + if (await Plugin.MuteManager.Mute(gameEvent.Owner, gameEvent.Origin, gameEvent.Target, expiration, reason)) + { + gameEvent.Origin.Tell(_translationLookup["PLUGINS_MUTE_COMMANDS_TEMPMUTE_TEMPMUTED"] + .FormatExt(gameEvent.Target.CleanedName)); + gameEvent.Target.Tell(_translationLookup["PLUGINS_MUTE_COMMANDS_TEMPMUTE_TARGET_TEMPMUTED"] + .FormatExt(expiration.HumanizeForCurrentCulture(), reason)); + return; + } + + gameEvent.Origin.Tell(_translationLookup["PLUGINS_MUTE_COMMANDS_MUTE_NOT_UNMUTED"] + .FormatExt(gameEvent.Target.CleanedName)); + return; + } + + gameEvent.Origin.Tell(_translationLookup["PLUGINS_MUTE_COMMANDS_TEMPMUTE_BAD_FORMAT"]); + } +} diff --git a/Plugins/Mute/Commands/UnmuteCommand.cs b/Plugins/Mute/Commands/UnmuteCommand.cs new file mode 100644 index 000000000..fd0aead25 --- /dev/null +++ b/Plugins/Mute/Commands/UnmuteCommand.cs @@ -0,0 +1,49 @@ +using Data.Models.Client; +using SharedLibraryCore; +using SharedLibraryCore.Commands; +using SharedLibraryCore.Configuration; +using SharedLibraryCore.Interfaces; + +namespace Mute.Commands; + +public class UnmuteCommand : Command +{ + public UnmuteCommand(CommandConfiguration config, ITranslationLookup translationLookup) : base(config, + translationLookup) + { + Name = "unmute"; + Description = translationLookup["PLUGINS_MUTE_COMMANDS_UNMUTE_DESC"]; + Alias = "um"; + Permission = EFClient.Permission.Moderator; + RequiresTarget = true; + SupportedGames = Plugin.SupportedGames; + Arguments = new[] + { + new CommandArgument + { + Name = translationLookup["COMMANDS_ARGS_PLAYER"], + Required = true + }, + new CommandArgument + { + Name = translationLookup["COMMANDS_ARGS_REASON"], + Required = true + } + }; + } + + public override async Task ExecuteAsync(GameEvent gameEvent) + { + if (await Plugin.MuteManager.Unmute(gameEvent.Owner, gameEvent.Origin, gameEvent.Target, gameEvent.Data)) + { + gameEvent.Origin.Tell(_translationLookup["PLUGINS_MUTE_COMMANDS_UNMUTE_UNMUTED"] + .FormatExt(gameEvent.Target.CleanedName)); + gameEvent.Target.Tell(_translationLookup["PLUGINS_MUTE_COMMANDS_UNMUTE_TARGET_UNMUTED"] + .FormatExt(gameEvent.Data)); + return; + } + + gameEvent.Origin.Tell(_translationLookup["PLUGINS_MUTE_COMMANDS_UNMUTE_NOT_MUTED"] + .FormatExt(gameEvent.Target.CleanedName)); + } +} diff --git a/Plugins/Mute/DataManager.cs b/Plugins/Mute/DataManager.cs deleted file mode 100644 index 593707d4e..000000000 --- a/Plugins/Mute/DataManager.cs +++ /dev/null @@ -1,38 +0,0 @@ -using Data.Models.Client; -using SharedLibraryCore.Interfaces; -using static System.Enum; - -namespace Mute; - -public class DataManager -{ - public DataManager(IMetaServiceV2 metaService) - { - _metaService = metaService; - } - - private readonly IMetaServiceV2 _metaService; - - public async Task ReadPersistentData(EFClient client) - { - var clientMuteState = client.GetAdditionalProperty(Plugin.MuteKey) ?? - Parse((await _metaService.GetPersistentMeta(Plugin.MuteKey, client.ClientId))? - .Value ?? nameof(MuteState.Unmuted)); - - client.SetAdditionalProperty(Plugin.MuteKey, clientMuteState); - return clientMuteState; - } - - public async Task WritePersistentData(EFClient client, MuteState state) - { - await _metaService.SetPersistentMeta(Plugin.MuteKey, state.ToString(), client.ClientId); - client.SetAdditionalProperty(Plugin.MuteKey, state); - } -} - -public enum MuteState -{ - Muted, - Unmuting, - Unmuted -} diff --git a/Plugins/Mute/Mute.csproj b/Plugins/Mute/Mute.csproj index e9a3113bf..d50971351 100644 --- a/Plugins/Mute/Mute.csproj +++ b/Plugins/Mute/Mute.csproj @@ -11,9 +11,9 @@ - + - + diff --git a/Plugins/Mute/MuteManager.cs b/Plugins/Mute/MuteManager.cs index 12252626b..7a688ad9d 100644 --- a/Plugins/Mute/MuteManager.cs +++ b/Plugins/Mute/MuteManager.cs @@ -1,21 +1,175 @@ -using SharedLibraryCore; +using Data.Models; +using Microsoft.Extensions.Logging; +using SharedLibraryCore; +using SharedLibraryCore.Database.Models; +using SharedLibraryCore.Interfaces; +using static System.Enum; +using ILogger = Microsoft.Extensions.Logging.ILogger; namespace Mute; public class MuteManager { - public async Task Mute(GameEvent gameEvent) + private readonly IMetaServiceV2 _metaService; + private readonly ITranslationLookup _translationLookup; + private readonly ILogger _logger; + + public MuteManager(IMetaServiceV2 metaService, ITranslationLookup translationLookup, ILogger logger) { - if (await Plugin.DataManager.ReadPersistentData(gameEvent.Target) == MuteState.Muted) + _metaService = metaService; + _translationLookup = translationLookup; + _logger = logger; + } + + public static bool IsExpiredMute(MuteStateMeta muteStateMeta) => + muteStateMeta.Expiration is not null && muteStateMeta.Expiration < DateTime.UtcNow; + + public async Task GetCurrentMuteState(EFClient client) + { + var clientMuteMeta = await ReadPersistentDataV2(client); + if (clientMuteMeta is not null) return clientMuteMeta; + + // Return null if the client doesn't have old or new meta. + var muteState = await ReadPersistentDataV1(client); + clientMuteMeta = new MuteStateMeta { - await gameEvent.Owner.ExecuteCommandAsync($"unmute {gameEvent.Target.ClientNumber}"); - await Plugin.DataManager.WritePersistentData(gameEvent.Target, - gameEvent.Target.IsIngame ? MuteState.Unmuted : MuteState.Unmuting); - return false; + ClientId = client.ClientId, + Reason = muteState is null ? string.Empty : _translationLookup["PLUGINS_MUTE_MIGRATED"], + Expiration = muteState switch + { + null => DateTime.UtcNow, + MuteState.Muted => null, + _ => DateTime.UtcNow + }, + AdminId = Utilities.IW4MAdminClient().ClientId, + MuteState = muteState ?? MuteState.Unmuted, + CommandExecuted = true + }; + + // Migrate old mute meta, else, client has no state, so set a generic one, but don't write it to database. + if (muteState is not null) + { + await WritePersistentData(client, clientMuteMeta); + } + else + { + client.SetAdditionalProperty(Plugin.MuteKey, clientMuteMeta); } - await gameEvent.Owner.ExecuteCommandAsync($"muteClient {gameEvent.Target.ClientNumber}"); - await Plugin.DataManager.WritePersistentData(gameEvent.Target, MuteState.Muted); + return clientMuteMeta; + } + + public async Task Mute(Server server, EFClient origin, EFClient target, DateTime? dateTime, string reason) + { + var clientMuteMeta = await GetCurrentMuteState(target); + if (clientMuteMeta.MuteState is MuteState.Muted && clientMuteMeta.CommandExecuted) return false; + + var newPenalty = new EFPenalty + { + Type = dateTime is null ? EFPenalty.PenaltyType.Mute : EFPenalty.PenaltyType.TempMute, + Expires = dateTime, + Offender = target, + Offense = reason, + Punisher = origin, + Link = target.AliasLink + }; + _logger.LogDebug("Creating new mute for {Target} with reason {Reason}", target.Name, reason); + await newPenalty.TryCreatePenalty(Plugin.Manager.GetPenaltyService(), _logger); + + clientMuteMeta = new MuteStateMeta + { + ClientId = target.ClientId, + Expiration = dateTime, + MuteState = MuteState.Muted, + Reason = reason, + AdminId = origin.ClientId, + CommandExecuted = false + }; + await WritePersistentData(target, clientMuteMeta); + + // Handle game command + var client = server.GetClientsAsList().FirstOrDefault(client => client.NetworkId == target.NetworkId); + await PerformGameCommand(server, client, clientMuteMeta); + return true; } + + public async Task Unmute(Server server, EFClient origin, EFClient target, string reason) + { + var clientMuteMeta = await GetCurrentMuteState(target); + if (clientMuteMeta.MuteState is MuteState.Unmuted && clientMuteMeta.CommandExecuted) return false; + if (!target.IsIngame && clientMuteMeta.MuteState is MuteState.Unmuting) return false; + + var newPenalty = new EFPenalty + { + Type = EFPenalty.PenaltyType.Unmute, + Expires = DateTime.Now, + Offender = target, + Offense = reason, + Punisher = origin, + Active = true, + Link = target.AliasLink + }; + _logger.LogDebug("Creating new unmute for {Target} with reason {Reason}", target.Name, reason); + await newPenalty.TryCreatePenalty(Plugin.Manager.GetPenaltyService(), _logger); + + clientMuteMeta = new MuteStateMeta + { + ClientId = target.ClientId, + Expiration = DateTime.UtcNow, + MuteState = target.IsIngame ? MuteState.Unmuted : MuteState.Unmuting, + Reason = reason, + AdminId = origin.ClientId, + CommandExecuted = false + }; + await WritePersistentData(target, clientMuteMeta); + + // Handle game command + var client = server.GetClientsAsList().FirstOrDefault(client => client.NetworkId == target.NetworkId); + await PerformGameCommand(server, client, clientMuteMeta); + + return true; + } + + public static async Task PerformGameCommand(Server server, EFClient? client, MuteStateMeta muteStateMeta) + { + if (client is null || !client.IsIngame) return; + + switch (muteStateMeta.MuteState) + { + case MuteState.Muted: + await server.ExecuteCommandAsync($"muteClient {client.ClientNumber}"); + muteStateMeta.CommandExecuted = true; + break; + case MuteState.Unmuted: + await server.ExecuteCommandAsync($"unmute {client.ClientNumber}"); + muteStateMeta.CommandExecuted = true; + break; + } + } + + private async Task ReadPersistentDataV1(EFClient client) => TryParse( + (await _metaService.GetPersistentMeta(Plugin.MuteKey, client.ClientId))?.Value, + out var muteState) + ? muteState + : null; + + private async Task ReadPersistentDataV2(EFClient client) + { + // Get meta from client + var clientMuteMeta = client.GetAdditionalProperty(Plugin.MuteKey); + if (clientMuteMeta is not null) return clientMuteMeta; + + // Get meta from database and store in client if exists + clientMuteMeta = await _metaService.GetPersistentMetaValue(Plugin.MuteKey, client.ClientId); + if (clientMuteMeta is not null) client.SetAdditionalProperty(Plugin.MuteKey, clientMuteMeta); + + return clientMuteMeta; + } + + private async Task WritePersistentData(EFClient client, MuteStateMeta clientMuteMeta) + { + client.SetAdditionalProperty(Plugin.MuteKey, clientMuteMeta); + await _metaService.SetPersistentMetaValue(Plugin.MuteKey, clientMuteMeta, client.ClientId); + } } diff --git a/Plugins/Mute/MuteStateMeta.cs b/Plugins/Mute/MuteStateMeta.cs new file mode 100644 index 000000000..92a57930a --- /dev/null +++ b/Plugins/Mute/MuteStateMeta.cs @@ -0,0 +1,21 @@ +using Newtonsoft.Json; + +namespace Mute; + +public class MuteStateMeta +{ + public int ClientId { get; set; } + public string Reason { get; set; } = string.Empty; + public DateTime? Expiration { get; set; } + public int AdminId { get; set; } + public MuteState MuteState { get; set; } + [JsonIgnore] + public bool CommandExecuted { get; set; } +} + +public enum MuteState +{ + Muted, + Unmuting, + Unmuted +} diff --git a/Plugins/Mute/Plugin.cs b/Plugins/Mute/Plugin.cs index e8af5a2e2..da71135fc 100644 --- a/Plugins/Mute/Plugin.cs +++ b/Plugins/Mute/Plugin.cs @@ -1,47 +1,111 @@ -using SharedLibraryCore; +using Microsoft.Extensions.Logging; +using SharedLibraryCore; +using SharedLibraryCore.Commands; using SharedLibraryCore.Database.Models; using SharedLibraryCore.Helpers; using SharedLibraryCore.Interfaces; +using JsonSerializer = System.Text.Json.JsonSerializer; namespace Mute; public class Plugin : IPlugin { - private readonly IInteractionRegistration _interactionRegistration; - private static readonly string MuteInteraction = nameof(MuteInteraction); - - public Plugin(IMetaServiceV2 metaService, IInteractionRegistration interactionRegistration) - { - _interactionRegistration = interactionRegistration; - DataManager = new DataManager(metaService); - } - public string Name => "Mute"; public float Version => (float) Utilities.GetVersionAsDouble(); public string Author => "Amos"; - public static string MuteKey = "IW4MMute"; - - public static DataManager DataManager; + public const string MuteKey = "IW4MMute"; + public static MuteManager MuteManager { get; private set; } = null!; + 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 static readonly string MuteInteraction = nameof(MuteInteraction); + + public Plugin(IMetaServiceV2 metaService, IInteractionRegistration interactionRegistration, + ITranslationLookup translationLookup, ILogger logger) + { + _interactionRegistration = interactionRegistration; + MuteManager = new MuteManager(metaService, translationLookup, logger); + } public async Task OnEventAsync(GameEvent gameEvent, Server server) { if (!SupportedGames.Contains(server.GameName)) return; + switch (gameEvent.Type) { + case GameEvent.EventType.Command: + + break; case GameEvent.EventType.Join: - switch (await DataManager.ReadPersistentData(gameEvent.Origin)) + // Check if user has any meta set, else ignore (unmuted) + var muteMetaJoin = await MuteManager.GetCurrentMuteState(gameEvent.Origin); + + switch (muteMetaJoin.MuteState) { case MuteState.Muted: - await server.ExecuteCommandAsync($"muteClient {gameEvent.Origin.ClientNumber}"); + // Let the client know when their mute expires. + gameEvent.Origin.Tell(Utilities.CurrentLocalization + .LocalizationIndex["PLUGINS_MUTE_REMAINING_TIME"].FormatExt( + muteMetaJoin.Expiration is not null + ? muteMetaJoin.Expiration.Value.HumanizeForCurrentCulture() + : Utilities.CurrentLocalization.LocalizationIndex["PLUGINS_MUTE_NEVER"], + muteMetaJoin.Reason)); break; case MuteState.Unmuting: - await server.ExecuteCommandAsync($"unmute {gameEvent.Origin.ClientNumber}"); - await DataManager.WritePersistentData(gameEvent.Origin, MuteState.Unmuted); + // Handle unmute of unmuted players. + await MuteManager.Unmute(server, Utilities.IW4MAdminClient(), gameEvent.Origin, + muteMetaJoin.Reason); + gameEvent.Origin.Tell(Utilities.CurrentLocalization + .LocalizationIndex["PLUGINS_MUTE_COMMANDS_UNMUTE_TARGET_UNMUTED"] + .FormatExt(muteMetaJoin.Reason)); break; - case MuteState.Unmuted: + } + + break; + case GameEvent.EventType.Say: + var muteMetaSay = await MuteManager.GetCurrentMuteState(gameEvent.Origin); + + switch (muteMetaSay.MuteState) + { + case MuteState.Muted: + // Let the client know when their mute expires. + gameEvent.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)); + break; + } + + break; + case GameEvent.EventType.Update: + // Get correct EFClient object + var client = server.GetClientsAsList() + .FirstOrDefault(client => client.NetworkId == gameEvent.Origin.NetworkId); + if (client == null) break; + + var muteMetaUpdate = await MuteManager.GetCurrentMuteState(client); + if (!muteMetaUpdate.CommandExecuted) + { + await MuteManager.PerformGameCommand(server, client, muteMetaUpdate); + } + + switch (muteMetaUpdate.MuteState) + { + case MuteState.Muted: + // Handle unmute if expired. + if (MuteManager.IsExpiredMute(muteMetaUpdate)) + { + await MuteManager.Unmute(server, Utilities.IW4MAdminClient(), client, + Utilities.CurrentLocalization.LocalizationIndex["PLUGINS_MUTE_EXPIRED"]); + client.Tell( + Utilities.CurrentLocalization.LocalizationIndex["PLUGINS_MUTE_TARGET_EXPIRED"]); + } + break; } @@ -51,16 +115,30 @@ public class Plugin : IPlugin public Task OnLoadAsync(IManager manager) { + Manager = manager; + + manager.CommandInterceptors.Add(gameEvent => + { + if (gameEvent.Extra is not Command command) return true; + return !DisabledCommands.Contains(command.GetType().Name) && !command.IsBroadcast; + }); + _interactionRegistration.RegisterInteraction(MuteInteraction, async (clientId, game, token) => { - if (!clientId.HasValue || game.HasValue && !SupportedGames.Contains((Server.Game)game.Value)) + if (!clientId.HasValue || game.HasValue && !SupportedGames.Contains((Server.Game) game.Value)) { return null; } - var muteState = await DataManager.ReadPersistentData(new EFClient { ClientId = clientId.Value }); + var reasonInput = new {Name = "Reason", Placeholder = "Reason", TargetId = clientId}; + var durationInput = new {Name = "Length", Placeholder = "Length", TargetId = clientId}; + var inputs = new[] {reasonInput, durationInput}; + var inputsJson = JsonSerializer.Serialize(inputs); - return muteState is MuteState.Unmuted or MuteState.Unmuting + var clientMuteMetaState = (await MuteManager.GetCurrentMuteState(new EFClient {ClientId = clientId.Value})) + .MuteState; + + return clientMuteMetaState is MuteState.Unmuted or MuteState.Unmuting ? new InteractionData { EntityId = clientId, @@ -69,11 +147,19 @@ public class Plugin : IPlugin ActionPath = "DynamicAction", ActionMeta = new() { - { "InteractionId", "command" }, - { "Data", $"mute @{clientId.Value}" }, - { "ActionButtonLabel", Utilities.CurrentLocalization.LocalizationIndex["WEBFRONT_PROFILE_CONTEXT_MENU_ACTION_MUTE"] }, - { "Name", Utilities.CurrentLocalization.LocalizationIndex["WEBFRONT_PROFILE_CONTEXT_MENU_ACTION_MUTE"] }, - { "ShouldRefresh", true.ToString() } + {"InteractionId", "command"}, + {"Data", $"mute @{clientId.Value}"}, + {"Outputs", $"{reasonInput.Name},{durationInput.Name}"}, + {"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 @@ -81,22 +167,32 @@ public class Plugin : IPlugin : new InteractionData { EntityId = clientId, - Name = Utilities.CurrentLocalization.LocalizationIndex["WEBFRONT_PROFILE_CONTEXT_MENU_ACTION_UNMUTE"], + Name = Utilities.CurrentLocalization.LocalizationIndex[ + "WEBFRONT_PROFILE_CONTEXT_MENU_ACTION_UNMUTE"], DisplayMeta = "oi-volume-high", ActionPath = "DynamicAction", ActionMeta = new() { - { "InteractionId", "command" }, - { "Data", $"mute @{clientId.Value}" }, - { "ActionButtonLabel", Utilities.CurrentLocalization.LocalizationIndex["WEBFRONT_PROFILE_CONTEXT_MENU_ACTION_UNMUTE"] }, - { "Name", Utilities.CurrentLocalization.LocalizationIndex["WEBFRONT_PROFILE_CONTEXT_MENU_ACTION_UNMUTE"] }, - { "ShouldRefresh", true.ToString() } + {"InteractionId", "command"}, + {"Data", $"mute @{clientId.Value}"}, + {"Outputs", $"{reasonInput.Name},{durationInput.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 }; }); - return Task.CompletedTask; } diff --git a/SharedLibraryCore/Interfaces/IManager.cs b/SharedLibraryCore/Interfaces/IManager.cs index 2de44f94c..c87e70979 100644 --- a/SharedLibraryCore/Interfaces/IManager.cs +++ b/SharedLibraryCore/Interfaces/IManager.cs @@ -103,7 +103,7 @@ namespace SharedLibraryCore.Interfaces /// event executed when event has finished executing /// event EventHandler OnGameEventExecuted; - + IAlertManager AlertManager { get; } } } diff --git a/WebfrontCore/wwwroot/css/src/profile.scss b/WebfrontCore/wwwroot/css/src/profile.scss index d5e0abdde..34549aa75 100644 --- a/WebfrontCore/wwwroot/css/src/profile.scss +++ b/WebfrontCore/wwwroot/css/src/profile.scss @@ -103,6 +103,21 @@ color: rgba(116, 147, 99, 1); } +.penalties-color-unmute { + color: #749363; + color: rgba(180, 222, 12, 1); +} + +.penalties-color-mute { + color: #749363; + color: rgba(222, 222, 12, 1); +} + +.penalties-color-tempmute { + color: #749363; + color: rgba(222, 145, 12, 1); +} + .penalties-color-unflag { color: rgb(140, 154, 132); }