add configurable command and broadcast command prefix for issue #149

This commit is contained in:
RaidMax 2020-07-31 20:40:03 -05:00
parent 6155493181
commit 04a95aa58a
27 changed files with 403 additions and 61 deletions

View File

@ -81,7 +81,7 @@ namespace IW4MAdmin.Application
ConfigHandler = appConfigHandler; ConfigHandler = appConfigHandler;
StartTime = DateTime.UtcNow; StartTime = DateTime.UtcNow;
PageList = new PageList(); PageList = new PageList();
AdditionalEventParsers = new List<IEventParser>() { new BaseEventParser(parserRegexFactory, logger) }; AdditionalEventParsers = new List<IEventParser>() { new BaseEventParser(parserRegexFactory, logger, appConfigHandler.Configuration()) };
AdditionalRConParsers = new List<IRConParser>() { new BaseRConParser(parserRegexFactory) }; AdditionalRConParsers = new List<IRConParser>() { new BaseRConParser(parserRegexFactory) };
TokenAuthenticator = new TokenAuthentication(); TokenAuthenticator = new TokenAuthentication();
_logger = logger; _logger = logger;
@ -432,6 +432,11 @@ namespace IW4MAdmin.Application
commandsToAddToConfig.AddRange(unsavedCommands); commandsToAddToConfig.AddRange(unsavedCommands);
} }
// this is because I want to store the command prefix in IW4MAdminSettings, but can't easily
// inject it to all the places that need it
cmdConfig.CommandPrefix = config.CommandPrefix;
cmdConfig.BroadcastCommandPrefix = config.BroadcastCommandPrefix;
foreach (var cmd in commandsToAddToConfig) foreach (var cmd in commandsToAddToConfig)
{ {
cmdConfig.Commands.Add(cmd.CommandConfigNameForType(), cmdConfig.Commands.Add(cmd.CommandConfigNameForType(),
@ -729,7 +734,7 @@ namespace IW4MAdmin.Application
public IEventParser GenerateDynamicEventParser(string name) public IEventParser GenerateDynamicEventParser(string name)
{ {
return new DynamicEventParser(_parserRegexFactory, _logger) return new DynamicEventParser(_parserRegexFactory, _logger, ConfigHandler.Configuration())
{ {
Name = name Name = name
}; };

View File

@ -1,4 +1,5 @@
using SharedLibraryCore; using SharedLibraryCore;
using SharedLibraryCore.Configuration;
using SharedLibraryCore.Database.Models; using SharedLibraryCore.Database.Models;
using SharedLibraryCore.Interfaces; using SharedLibraryCore.Interfaces;
using System; using System;
@ -12,11 +13,13 @@ namespace IW4MAdmin.Application.EventParsers
{ {
private readonly Dictionary<string, (string, Func<string, IEventParserConfiguration, GameEvent, GameEvent>)> _customEventRegistrations; private readonly Dictionary<string, (string, Func<string, IEventParserConfiguration, GameEvent, GameEvent>)> _customEventRegistrations;
private readonly ILogger _logger; private readonly ILogger _logger;
private readonly ApplicationConfiguration _appConfig;
public BaseEventParser(IParserRegexFactory parserRegexFactory, ILogger logger) public BaseEventParser(IParserRegexFactory parserRegexFactory, ILogger logger, ApplicationConfiguration appConfig)
{ {
_customEventRegistrations = new Dictionary<string, (string, Func<string, IEventParserConfiguration, GameEvent, GameEvent>)>(); _customEventRegistrations = new Dictionary<string, (string, Func<string, IEventParserConfiguration, GameEvent, GameEvent>)>();
_logger = logger; _logger = logger;
_appConfig = appConfig;
Configuration = new DynamicEventParserConfiguration(parserRegexFactory) Configuration = new DynamicEventParserConfiguration(parserRegexFactory)
{ {
@ -128,7 +131,7 @@ namespace IW4MAdmin.Application.EventParsers
int clientNumber = int.Parse(matchResult.Values[Configuration.Say.GroupMapping[ParserRegex.GroupType.OriginClientNumber]]); int clientNumber = int.Parse(matchResult.Values[Configuration.Say.GroupMapping[ParserRegex.GroupType.OriginClientNumber]]);
// todo: these need to defined outside of here // todo: these need to defined outside of here
if (message[0] == '!' || message[0] == '@') if (message.StartsWith(_appConfig.CommandPrefix) || message.StartsWith(_appConfig.BroadcastCommandPrefix))
{ {
return new GameEvent() return new GameEvent()
{ {

View File

@ -1,4 +1,5 @@
using SharedLibraryCore.Interfaces; using SharedLibraryCore.Configuration;
using SharedLibraryCore.Interfaces;
namespace IW4MAdmin.Application.EventParsers namespace IW4MAdmin.Application.EventParsers
{ {
@ -8,7 +9,7 @@ namespace IW4MAdmin.Application.EventParsers
/// </summary> /// </summary>
sealed internal class DynamicEventParser : BaseEventParser sealed internal class DynamicEventParser : BaseEventParser
{ {
public DynamicEventParser(IParserRegexFactory parserRegexFactory, ILogger logger) : base(parserRegexFactory, logger) public DynamicEventParser(IParserRegexFactory parserRegexFactory, ILogger logger, ApplicationConfiguration appConfig) : base(parserRegexFactory, logger, appConfig)
{ {
} }
} }

View File

@ -139,7 +139,7 @@ namespace IW4MAdmin
{ {
try try
{ {
C = await SharedLibraryCore.Commands.CommandProcessing.ValidateCommand(E); C = await SharedLibraryCore.Commands.CommandProcessing.ValidateCommand(E, Manager.GetApplicationSettings().Configuration());
} }
catch (CommandException e) catch (CommandException e)

View File

@ -59,7 +59,7 @@ namespace SharedLibraryCore
/// <summary> /// <summary>
/// Helper property to provide the syntax of the command /// Helper property to provide the syntax of the command
/// </summary> /// </summary>
public string Syntax => $"{_translationLookup["COMMAND_HELP_SYNTAX"]} !{Alias} {string.Join(" ", Arguments.Select(a => $"<{(a.Required ? "" : _translationLookup["COMMAND_HELP_OPTIONAL"] + " ")}{a.Name}>"))}"; public string Syntax => $"{_translationLookup["COMMAND_HELP_SYNTAX"]} {_config.CommandPrefix}{Alias} {string.Join(" ", Arguments.Select(a => $"<{(a.Required ? "" : _translationLookup["COMMAND_HELP_OPTIONAL"] + " ")}{a.Name}>"))}";
/// <summary> /// <summary>
/// Alternate name for this command to be executed by /// Alternate name for this command to be executed by
@ -123,5 +123,7 @@ namespace SharedLibraryCore
/// indicates if this command allows impersonation (run as) /// indicates if this command allows impersonation (run as)
/// </summary> /// </summary>
public bool AllowImpersonation { get; set; } public bool AllowImpersonation { get; set; }
public bool IsBroadcast { get; set; }
} }
} }

View File

@ -1,4 +1,5 @@
using SharedLibraryCore.Database.Models; using SharedLibraryCore.Configuration;
using SharedLibraryCore.Database.Models;
using SharedLibraryCore.Exceptions; using SharedLibraryCore.Exceptions;
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
@ -10,12 +11,14 @@ namespace SharedLibraryCore.Commands
{ {
public class CommandProcessing public class CommandProcessing
{ {
public static async Task<Command> ValidateCommand(GameEvent E) public static async Task<Command> ValidateCommand(GameEvent E, ApplicationConfiguration appConfig)
{ {
var loc = Utilities.CurrentLocalization.LocalizationIndex; var loc = Utilities.CurrentLocalization.LocalizationIndex;
var Manager = E.Owner.Manager; var Manager = E.Owner.Manager;
bool isBroadcast = E.Data.StartsWith(appConfig.BroadcastCommandPrefix);
int prefixLength = isBroadcast ? appConfig.BroadcastCommandPrefix.Length : appConfig.CommandPrefix.Length;
string CommandString = E.Data.Substring(1, E.Data.Length - 1).Split(' ')[0]; string CommandString = E.Data.Substring(prefixLength, E.Data.Length - prefixLength).Split(' ')[0];
E.Message = E.Data; E.Message = E.Data;
Command C = null; Command C = null;
@ -34,6 +37,8 @@ namespace SharedLibraryCore.Commands
throw new CommandException($"{E.Origin} entered unknown command \"{CommandString}\""); throw new CommandException($"{E.Origin} entered unknown command \"{CommandString}\"");
} }
C.IsBroadcast = isBroadcast;
if (!C.AllowImpersonation && E.ImpersonationOrigin != null) if (!C.AllowImpersonation && E.ImpersonationOrigin != null)
{ {
E.ImpersonationOrigin.Tell(loc["COMMANDS_RUN_AS_FAIL"]); E.ImpersonationOrigin.Tell(loc["COMMANDS_RUN_AS_FAIL"]);

View File

@ -773,6 +773,8 @@ namespace SharedLibraryCore.Commands
/// </summary> /// </summary>
public class ListAdminsCommand : Command public class ListAdminsCommand : Command
{ {
private readonly CommandConfiguration _config;
public ListAdminsCommand(CommandConfiguration config, ITranslationLookup translationLookup) : base(config, translationLookup) public ListAdminsCommand(CommandConfiguration config, ITranslationLookup translationLookup) : base(config, translationLookup)
{ {
Name = "admins"; Name = "admins";
@ -780,6 +782,8 @@ namespace SharedLibraryCore.Commands
Alias = "a"; Alias = "a";
Permission = Permission.User; Permission = Permission.User;
RequiresTarget = false; RequiresTarget = false;
_config = config;
} }
public static string OnlineAdmins(Server S, ITranslationLookup lookup) public static string OnlineAdmins(Server S, ITranslationLookup lookup)
@ -798,7 +802,7 @@ namespace SharedLibraryCore.Commands
{ {
foreach (string line in OnlineAdmins(E.Owner, _translationLookup).Split(Environment.NewLine)) foreach (string line in OnlineAdmins(E.Owner, _translationLookup).Split(Environment.NewLine))
{ {
var _ = E.Message.IsBroadcastCommand() ? E.Owner.Broadcast(line) : E.Origin.Tell(line); var _ = E.Message.IsBroadcastCommand(_config.BroadcastCommandPrefix) ? E.Owner.Broadcast(line) : E.Origin.Tell(line);
} }
return Task.CompletedTask; return Task.CompletedTask;
@ -903,6 +907,8 @@ namespace SharedLibraryCore.Commands
/// </summary> /// </summary>
public class ListRulesCommands : Command public class ListRulesCommands : Command
{ {
private readonly CommandConfiguration _config;
public ListRulesCommands(CommandConfiguration config, ITranslationLookup translationLookup) : base(config, translationLookup) public ListRulesCommands(CommandConfiguration config, ITranslationLookup translationLookup) : base(config, translationLookup)
{ {
Name = "rules"; Name = "rules";
@ -910,6 +916,8 @@ namespace SharedLibraryCore.Commands
Alias = "r"; Alias = "r";
Permission = Permission.User; Permission = Permission.User;
RequiresTarget = false; RequiresTarget = false;
_config = config;
} }
public override Task ExecuteAsync(GameEvent E) public override Task ExecuteAsync(GameEvent E)
@ -917,7 +925,7 @@ namespace SharedLibraryCore.Commands
if (E.Owner.Manager.GetApplicationSettings().Configuration().GlobalRules?.Length < 1 && if (E.Owner.Manager.GetApplicationSettings().Configuration().GlobalRules?.Length < 1 &&
E.Owner.ServerConfig.Rules?.Length < 1) E.Owner.ServerConfig.Rules?.Length < 1)
{ {
var _ = E.Message.IsBroadcastCommand() ? var _ = E.Message.IsBroadcastCommand(_config.BroadcastCommandPrefix) ?
E.Owner.Broadcast(_translationLookup["COMMANDS_RULES_NONE"]) : E.Owner.Broadcast(_translationLookup["COMMANDS_RULES_NONE"]) :
E.Origin.Tell(_translationLookup["COMMANDS_RULES_NONE"]); E.Origin.Tell(_translationLookup["COMMANDS_RULES_NONE"]);
} }
@ -933,7 +941,7 @@ namespace SharedLibraryCore.Commands
foreach (string r in rules) foreach (string r in rules)
{ {
var _ = E.Message.IsBroadcastCommand() ? E.Owner.Broadcast($"- {r}") : E.Origin.Tell($"- {r}"); var _ = E.Message.IsBroadcastCommand(_config.BroadcastCommandPrefix) ? E.Owner.Broadcast($"- {r}") : E.Origin.Tell($"- {r}");
} }
} }

View File

@ -66,6 +66,12 @@ namespace SharedLibraryCore.Configuration
[LocalizedDisplayName("WEBFRONT_CONFIGURATION_CUSTOM_LOCALE")] [LocalizedDisplayName("WEBFRONT_CONFIGURATION_CUSTOM_LOCALE")]
public string CustomLocale { get; set; } public string CustomLocale { get; set; }
[LocalizedDisplayName("WEBFRONT_CONFIGURATION_COMMAND_PREFIX")]
public string CommandPrefix { get; set; } = "!";
[LocalizedDisplayName("WEBFRONT_CONFIGURATION_BROADCAST_COMMAND_PREFIX")]
public string BroadcastCommandPrefix { get; set; } = "@";
[LocalizedDisplayName("WEBFRONT_CONFIGURATION_DB_PROVIDER")] [LocalizedDisplayName("WEBFRONT_CONFIGURATION_DB_PROVIDER")]
public string DatabaseProvider { get; set; } = "sqlite"; public string DatabaseProvider { get; set; } = "sqlite";
[ConfigurationOptional] [ConfigurationOptional]

View File

@ -1,6 +1,7 @@
using SharedLibraryCore.Interfaces; using SharedLibraryCore.Interfaces;
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Text.Json.Serialization;
namespace SharedLibraryCore.Configuration namespace SharedLibraryCore.Configuration
{ {
@ -14,6 +15,18 @@ namespace SharedLibraryCore.Configuration
/// </summary> /// </summary>
public Dictionary<string, CommandProperties> Commands { get; set; } = new Dictionary<string, CommandProperties>(); public Dictionary<string, CommandProperties> Commands { get; set; } = new Dictionary<string, CommandProperties>();
/// <summary>
/// prefix indicated the chat message is a command
/// </summary>
[JsonIgnore]
public string CommandPrefix { get; set; }
/// <summary>
/// prefix indicating that the chat message is a broadcast command
/// </summary>
[JsonIgnore]
public string BroadcastCommandPrefix { get; set; }
public IBaseConfiguration Generate() public IBaseConfiguration Generate()
{ {
throw new NotImplementedException(); throw new NotImplementedException();

View File

@ -64,13 +64,19 @@ namespace SharedLibraryCore.Configuration.Validation
RuleFor(_app => _app.GlobalRules) RuleFor(_app => _app.GlobalRules)
.NotNull(); .NotNull();
RuleForEach(_app => _app.Servers)
.NotEmpty()
.SetValidator(new ServerConfigurationValidator());
RuleFor(_app => _app.MasterUrl) RuleFor(_app => _app.MasterUrl)
.NotNull() .NotNull()
.Must(_url => _url != null && _url.Scheme == Uri.UriSchemeHttp); .Must(_url => _url != null && _url.Scheme == Uri.UriSchemeHttp);
RuleFor(_app => _app.CommandPrefix)
.NotEmpty();
RuleFor(_app => _app.BroadcastCommandPrefix)
.NotEmpty();
RuleForEach(_app => _app.Servers)
.NotEmpty()
.SetValidator(new ServerConfigurationValidator());
} }
} }
} }

View File

@ -49,5 +49,10 @@ namespace SharedLibraryCore.Interfaces
/// Indicates if the commands can be run as another client /// Indicates if the commands can be run as another client
/// </summary> /// </summary>
bool AllowImpersonation { get; } bool AllowImpersonation { get; }
/// <summary>
/// Indicates if the command result should be broadcasted to all clients
/// </summary>
bool IsBroadcast { get; set; }
} }
} }

View File

@ -486,7 +486,7 @@ namespace SharedLibraryCore.Database.Models
.DisallowedClientNames .DisallowedClientNames
?.Any(_name => Regex.IsMatch(Name, _name)) ?? false) ?.Any(_name => Regex.IsMatch(Name, _name)) ?? false)
{ {
CurrentServer.Logger.WriteDebug($"Kicking {this} because their name is generic"); CurrentServer.Logger.WriteDebug($"Kicking {this} because their name is not allowed");
Kick(loc["SERVER_KICK_GENERICNAME"], Utilities.IW4MAdminClient(CurrentServer)); Kick(loc["SERVER_KICK_GENERICNAME"], Utilities.IW4MAdminClient(CurrentServer));
return false; return false;
} }

View File

@ -235,9 +235,14 @@ namespace SharedLibraryCore
return str; return str;
} }
public static bool IsBroadcastCommand(this string str) public static bool IsBroadcastCommand(this string str, string broadcastCommandPrefix)
{ {
return str[0] == '@'; return str.StartsWith(broadcastCommandPrefix);
}
public static IManagerCommand AsCommand(this GameEvent gameEvent)
{
return gameEvent.Extra as IManagerCommand;
} }
/// <summary> /// <summary>

View File

@ -22,6 +22,9 @@
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<None Update="Files\GameEvent.Command.CustomPrefix.json">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
<None Update="Files\GameEvents.json"> <None Update="Files\GameEvents.json">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory> <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None> </None>

View File

@ -6,6 +6,7 @@ using Microsoft.Extensions.DependencyInjection;
using Newtonsoft.Json; using Newtonsoft.Json;
using NUnit.Framework; using NUnit.Framework;
using SharedLibraryCore; using SharedLibraryCore;
using SharedLibraryCore.Configuration;
using SharedLibraryCore.Interfaces; using SharedLibraryCore.Interfaces;
using System; using System;
using static SharedLibraryCore.GameEvent; using static SharedLibraryCore.GameEvent;
@ -18,17 +19,20 @@ namespace ApplicationTests
private EventLogTest eventLogData; private EventLogTest eventLogData;
private IServiceProvider serviceProvider; private IServiceProvider serviceProvider;
private ILogger fakeLogger; private ILogger fakeLogger;
private ApplicationConfiguration appConfig;
[SetUp] [SetUp]
public void Setup() public void Setup()
{ {
eventLogData = JsonConvert.DeserializeObject<EventLogTest>(System.IO.File.ReadAllText("Files/GameEvents.json")); eventLogData = JsonConvert.DeserializeObject<EventLogTest>(System.IO.File.ReadAllText("Files/GameEvents.json"));
appConfig = ConfigurationGenerators.CreateApplicationConfiguration();
fakeLogger = A.Fake<ILogger>(); fakeLogger = A.Fake<ILogger>();
serviceProvider = new ServiceCollection() serviceProvider = new ServiceCollection()
.AddSingleton<BaseEventParser>() .AddSingleton<BaseEventParser>()
.AddTransient<IParserPatternMatcher, ParserPatternMatcher>() .AddTransient<IParserPatternMatcher, ParserPatternMatcher>()
.AddSingleton<IParserRegexFactory, ParserRegexFactory>() .AddSingleton<IParserRegexFactory, ParserRegexFactory>()
.AddSingleton(appConfig)
.AddSingleton(fakeLogger) .AddSingleton(fakeLogger)
.BuildServiceProvider(); .BuildServiceProvider();
} }
@ -38,22 +42,6 @@ namespace ApplicationTests
{ {
var eventParser = serviceProvider.GetService<BaseEventParser>(); var eventParser = serviceProvider.GetService<BaseEventParser>();
void AssertMatch(GameEvent src, LogEvent expected)
{
Assert.AreEqual(expected.ExpectedEventType, src.Type);
Assert.AreEqual(expected.ExpectedData, src.Data);
Assert.AreEqual(expected.ExpectedMessage, src.Message);
Assert.AreEqual(expected.ExpectedTime, src.GameTime);
//Assert.AreEqual(expected.ExpectedOriginClientName, src.Origin?.Name);
Assert.AreEqual(expected.ExpectedOriginClientNumber, src.Origin?.ClientNumber);
Assert.AreEqual(expected.ExpectedOriginNetworkId, src.Origin?.NetworkId.ToString("X"));
//Assert.AreEqual(expected.ExpectedTargetClientName, src.Target?.Name);
Assert.AreEqual(expected.ExpectedTargetClientNumber, src.Target?.ClientNumber);
Assert.AreEqual(expected.ExpectedTargetNetworkId, src.Target?.NetworkId.ToString("X"));
}
foreach (var e in eventLogData.Events) foreach (var e in eventLogData.Events)
{ {
var parsedEvent = eventParser.GenerateGameEvent(e.EventLine); var parsedEvent = eventParser.GenerateGameEvent(e.EventLine);
@ -109,5 +97,45 @@ namespace ApplicationTests
A.CallTo(() => fakeLogger.WriteWarning(A<string>.Ignored)) A.CallTo(() => fakeLogger.WriteWarning(A<string>.Ignored))
.MustHaveHappenedOnceExactly(); .MustHaveHappenedOnceExactly();
} }
[Test]
public void Test_CustomCommandPrefix_Parses()
{
var eventParser = serviceProvider.GetService<BaseEventParser>();
var commandData = JsonConvert.DeserializeObject<EventLogTest>(System.IO.File.ReadAllText("Files/GameEvent.Command.CustomPrefix.json"));
appConfig.CommandPrefix = "^^";
var e = commandData.Events[0];
var parsedEvent = eventParser.GenerateGameEvent(e.EventLine);
AssertMatch(parsedEvent, e);
}
[Test]
public void Test_CustomBroadcastCommandPrefix_Parses()
{
var eventParser = serviceProvider.GetService<BaseEventParser>();
var commandData = JsonConvert.DeserializeObject<EventLogTest>(System.IO.File.ReadAllText("Files/GameEvent.Command.CustomPrefix.json"));
appConfig.BroadcastCommandPrefix = "@@";
var e = commandData.Events[1];
var parsedEvent = eventParser.GenerateGameEvent(e.EventLine);
AssertMatch(parsedEvent, e);
}
private static void AssertMatch(GameEvent src, LogEvent expected)
{
Assert.AreEqual(expected.ExpectedEventType, src.Type);
Assert.AreEqual(expected.ExpectedData, src.Data);
Assert.AreEqual(expected.ExpectedMessage, src.Message);
Assert.AreEqual(expected.ExpectedTime, src.GameTime);
//Assert.AreEqual(expected.ExpectedOriginClientName, src.Origin?.Name);
Assert.AreEqual(expected.ExpectedOriginClientNumber, src.Origin?.ClientNumber);
Assert.AreEqual(expected.ExpectedOriginNetworkId, src.Origin?.NetworkId.ToString("X"));
//Assert.AreEqual(expected.ExpectedTargetClientName, src.Target?.Name);
Assert.AreEqual(expected.ExpectedTargetClientNumber, src.Target?.ClientNumber);
Assert.AreEqual(expected.ExpectedTargetNetworkId, src.Target?.NetworkId.ToString("X"));
}
} }
} }

View File

@ -57,7 +57,8 @@ namespace ApplicationTests
.Returns(new Command[] .Returns(new Command[]
{ {
new ImpersonatableCommand(cmdConfig, transLookup), new ImpersonatableCommand(cmdConfig, transLookup),
new NonImpersonatableCommand(cmdConfig, transLookup) new NonImpersonatableCommand(cmdConfig, transLookup),
new MockCommand(cmdConfig, transLookup)
}); });
A.CallTo(() => manager.AddEvent(A<GameEvent>.Ignored)) A.CallTo(() => manager.AddEvent(A<GameEvent>.Ignored))
@ -519,5 +520,22 @@ namespace ApplicationTests
Assert.IsNotEmpty(mockEventHandler.Events.Where(_event => _event.Type == GameEvent.EventType.ChangePermission && !_event.Failed)); Assert.IsNotEmpty(mockEventHandler.Events.Where(_event => _event.Type == GameEvent.EventType.ChangePermission && !_event.Failed));
} }
#endregion #endregion
#region PREFIX_PROCESSING
[Test]
public async Task Test_CommandProcessing_IsBroadcastCommand()
{
string broadcastPrefix = "@@";
var config = ConfigurationGenerators.CreateApplicationConfiguration();
config.BroadcastCommandPrefix = broadcastPrefix;
var server = serviceProvider.GetRequiredService<IW4MServer>();
var cmd = EventGenerators.GenerateEvent(GameEvent.EventType.Command, $"{broadcastPrefix}{nameof(MockCommand)}", server);
var result = await CommandProcessing.ValidateCommand(cmd, config);
Assert.AreEqual(nameof(MockCommand), result.Name);
Assert.IsTrue(result.IsBroadcast);
}
#endregion
} }
} }

View File

@ -0,0 +1,32 @@
{
"Events": [
{
"Game": "IW4",
"EventLine": " 12:34 say;AAA;1;Player;^^help",
"ExpectedEventType": "Command",
"ExpectedData": "^^help",
"ExpectedMessage": "^^help",
"ExpectedOriginNetworkId": "AAA",
"ExpectedOriginClientNumber": 1,
"ExpectedOriginClientName": "Player",
"ExpectedTargetNetworkId": null,
"ExpectedTargetClientNumber": null,
"ExpectedTargetClientName": null,
"ExpectedTime": 754
},
{
"Game": "IW4",
"EventLine": " 12:34 say;AAA;1;Player;@@help",
"ExpectedEventType": "Command",
"ExpectedData": "@@help",
"ExpectedMessage": "@@help",
"ExpectedOriginNetworkId": "AAA",
"ExpectedOriginClientNumber": 1,
"ExpectedOriginClientName": "Player",
"ExpectedTargetNetworkId": null,
"ExpectedTargetClientNumber": null,
"ExpectedTargetClientName": null,
"ExpectedTime": 754
}
]
}

View File

@ -0,0 +1,159 @@
{
"Events": [
{
"Game": "IW4",
"EventLine": " 12:34 say;AAA;1;Player;this is a test message",
"ExpectedEventType": "Say",
"ExpectedData": "this is a test message",
"ExpectedMessage": "this is a test message",
"ExpectedOriginNetworkId": "AAA",
"ExpectedOriginClientNumber": 1,
"ExpectedOriginClientName": "Player",
"ExpectedTargetNetworkId": null,
"ExpectedTargetClientNumber": null,
"ExpectedTargetClientName": null,
"ExpectedTime": 754
},
{
"Game": "IW4",
"EventLine": " 12:34 sayteam;AAA;1;Player;this is a test team message",
"ExpectedEventType": "Say",
"ExpectedData": "this is a test team message",
"ExpectedMessage": "this is a test team message",
"ExpectedOriginNetworkId": "AAA",
"ExpectedOriginClientNumber": 1,
"ExpectedOriginClientName": "Player",
"ExpectedTargetNetworkId": null,
"ExpectedTargetClientNumber": null,
"ExpectedTargetClientName": null,
"ExpectedTime": 754
},
{
"Game": "IW4",
"EventLine": " 12:34 say;AAA;1;Player;!help",
"ExpectedEventType": "Command",
"ExpectedData": "!help",
"ExpectedMessage": "!help",
"ExpectedOriginNetworkId": "AAA",
"ExpectedOriginClientNumber": 1,
"ExpectedOriginClientName": "Player",
"ExpectedTargetNetworkId": null,
"ExpectedTargetClientNumber": null,
"ExpectedTargetClientName": null,
"ExpectedTime": 754
},
{
"Game": "IW4",
"EventLine": "2824:03 K;BBB;6;axis;Snake;AAA;11;allies;Doctor;sa80_reflex_xmags_mp;55;MOD_HEAD_SHOT;head",
"ExpectedEventType": "Kill",
"ExpectedData": "K;BBB;6;axis;Snake;AAA;11;allies;Doctor;sa80_reflex_xmags_mp;55;MOD_HEAD_SHOT;head",
"ExpectedMessage": null,
"ExpectedOriginNetworkId": "AAA",
"ExpectedOriginClientNumber": 11,
"ExpectedOriginClientName": "Doctor",
"ExpectedTargetNetworkId": "BBB",
"ExpectedTargetClientNumber": 6,
"ExpectedTargetClientName": "Snake",
"ExpectedTime": 169443
},
{
"Game": "IW4",
"EventLine": "6:44 K;AAA;0;allies;Player;;-1;world;;none;100;MOD_FALLING;none",
"ExpectedEventType": "Kill",
"ExpectedData": "K;AAA;0;allies;Player;;-1;world;;none;100;MOD_FALLING;none",
"ExpectedMessage": null,
"ExpectedOriginNetworkId": "FFFFFFFFFFFFFFFF",
"ExpectedOriginClientNumber": -1,
"ExpectedOriginClientName": "",
"ExpectedTargetNetworkId": "AAA",
"ExpectedTargetClientNumber": 0,
"ExpectedTargetClientName": "Player",
"ExpectedTime": 404
},
{
"Game": "IW4",
"EventLine": "2824:03 D;BBB;6;axis;Snake;AAA;11;allies;Doctor;sa80_reflex_xmags_mp;55;MOD_HEAD_SHOT;head",
"ExpectedEventType": "Damage",
"ExpectedData": "D;BBB;6;axis;Snake;AAA;11;allies;Doctor;sa80_reflex_xmags_mp;55;MOD_HEAD_SHOT;head",
"ExpectedMessage": null,
"ExpectedOriginNetworkId": "AAA",
"ExpectedOriginClientNumber": 11,
"ExpectedOriginClientName": "Doctor",
"ExpectedTargetNetworkId": "BBB",
"ExpectedTargetClientNumber": 6,
"ExpectedTargetClientName": "Snake",
"ExpectedTime": 169443
},
{
"Game": "IW4",
"EventLine": "1:43 J;AAA;1;Doctor",
"ExpectedEventType": "Preconnect",
"ExpectedData": "J;AAA;1;Doctor",
"ExpectedMessage": null,
"ExpectedOriginNetworkId": "AAA",
"ExpectedOriginClientNumber": 1,
"ExpectedOriginClientName": "Doctor",
"ExpectedTargetNetworkId": null,
"ExpectedTargetClientNumber": null,
"ExpectedTargetClientName": null,
"ExpectedTime": 103
},
{
"Game": "IW4",
"EventLine": "1344:54 Q;AAA;1;Cosmic Riptide",
"ExpectedEventType": "Predisconnect",
"ExpectedData": "Q;AAA;1;Cosmic Riptide",
"ExpectedMessage": null,
"ExpectedOriginNetworkId": "AAA",
"ExpectedOriginClientNumber": 1,
"ExpectedOriginClientName": "Cosmic Riptide",
"ExpectedTargetNetworkId": null,
"ExpectedTargetClientNumber": null,
"ExpectedTargetClientName": null,
"ExpectedTime": 80694
},
{
"Game": "IW4",
"EventLine": "75:44 ExitLevel: executed",
"ExpectedEventType": "MapEnd",
"ExpectedData": "ExitLevel: executed",
"ExpectedMessage": null,
"ExpectedOriginNetworkId": 0,
"ExpectedOriginClientNumber": -1,
"ExpectedOriginClientName": null,
"ExpectedTargetNetworkId": 0,
"ExpectedTargetClientNumber": -1,
"ExpectedTargetClientName": null,
"ExpectedTime": 4544
},
{
"Game": "IW4",
"EventLine": " 75:44 InitGame",
"ExpectedEventType": "MapChange",
"ExpectedData": "InitGame",
"ExpectedMessage": null,
"ExpectedOriginNetworkId": 0,
"ExpectedOriginClientNumber": -1,
"ExpectedOriginClientName": null,
"ExpectedTargetNetworkId": 0,
"ExpectedTargetClientNumber": -1,
"ExpectedTargetClientName": null,
"ExpectedTime": 4544
},
{
"Game": "IW4",
"EventLine": " 0:00 CustomLogLine",
"ExpectedEventType": "Unknown",
"ExpectedData": "CustomLogLine",
"ExpectedMessage": null,
"ExpectedOriginNetworkId": 0,
"ExpectedOriginClientNumber": -1,
"ExpectedOriginClientName": null,
"ExpectedTargetNetworkId": 0,
"ExpectedTargetClientNumber": -1,
"ExpectedTargetClientName": null,
"ExpectedTime": 0
}
]
}

View File

@ -0,0 +1,26 @@
using SharedLibraryCore;
using System;
namespace ApplicationTests.Fixtures
{
static class EventGenerators
{
public static GameEvent GenerateEvent(GameEvent.EventType type, string data, Server owner)
{
switch (type)
{
case GameEvent.EventType.Command:
return new GameEvent
{
Origin = ClientGenerators.CreateDatabaseClient(),
Data = data,
Message = data,
Owner = owner
};
default:
throw new NotImplementedException();
}
}
}
}

View File

@ -1,7 +1,6 @@
using SharedLibraryCore; using SharedLibraryCore;
using SharedLibraryCore.Configuration; using SharedLibraryCore.Configuration;
using SharedLibraryCore.Interfaces; using SharedLibraryCore.Interfaces;
using System;
using System.Threading.Tasks; using System.Threading.Tasks;
namespace ApplicationTests.Mocks namespace ApplicationTests.Mocks
@ -33,4 +32,17 @@ namespace ApplicationTests.Mocks
return Task.CompletedTask; return Task.CompletedTask;
} }
} }
class MockCommand : Command
{
public MockCommand(CommandConfiguration config, ITranslationLookup lookup) : base(config, lookup)
{
Name = nameof(MockCommand);
}
public override Task ExecuteAsync(GameEvent E)
{
return Task.CompletedTask;
}
}
} }

View File

@ -3,6 +3,7 @@ using IW4MAdmin;
using IW4MAdmin.Application; using IW4MAdmin.Application;
using IW4MAdmin.Application.EventParsers; using IW4MAdmin.Application.EventParsers;
using NUnit.Framework; using NUnit.Framework;
using SharedLibraryCore.Configuration;
using SharedLibraryCore.Interfaces; using SharedLibraryCore.Interfaces;
using System; using System;
using System.Diagnostics; using System.Diagnostics;
@ -35,7 +36,7 @@ namespace ApplicationTests
new SharedLibraryCore.Configuration.ServerConfiguration() { IPAddress = "127.0.0.1", Port = 28960 }, new SharedLibraryCore.Configuration.ServerConfiguration() { IPAddress = "127.0.0.1", Port = 28960 },
A.Fake<ITranslationLookup>(), A.Fake<IRConConnectionFactory>(), A.Fake<IGameLogReaderFactory>()); A.Fake<ITranslationLookup>(), A.Fake<IRConConnectionFactory>(), A.Fake<IGameLogReaderFactory>());
var parser = new BaseEventParser(A.Fake<IParserRegexFactory>(), A.Fake<ILogger>()); var parser = new BaseEventParser(A.Fake<IParserRegexFactory>(), A.Fake<ILogger>(), A.Fake<ApplicationConfiguration>());
parser.Configuration.GuidNumberStyle = System.Globalization.NumberStyles.Integer; parser.Configuration.GuidNumberStyle = System.Globalization.NumberStyles.Integer;
var log = System.IO.File.ReadAllLines("Files\\T6MapRotation.log"); var log = System.IO.File.ReadAllLines("Files\\T6MapRotation.log");
@ -61,7 +62,7 @@ namespace ApplicationTests
new SharedLibraryCore.Configuration.ServerConfiguration() { IPAddress = "127.0.0.1", Port = 28960 }, new SharedLibraryCore.Configuration.ServerConfiguration() { IPAddress = "127.0.0.1", Port = 28960 },
A.Fake<ITranslationLookup>(), A.Fake<IRConConnectionFactory>(), A.Fake<IGameLogReaderFactory>()); A.Fake<ITranslationLookup>(), A.Fake<IRConConnectionFactory>(), A.Fake<IGameLogReaderFactory>());
var parser = new BaseEventParser(A.Fake<IParserRegexFactory>(), A.Fake<ILogger>()); var parser = new BaseEventParser(A.Fake<IParserRegexFactory>(), A.Fake<ILogger>(), A.Fake<ApplicationConfiguration>());
parser.Configuration.GuidNumberStyle = System.Globalization.NumberStyles.Integer; parser.Configuration.GuidNumberStyle = System.Globalization.NumberStyles.Integer;
var log = System.IO.File.ReadAllLines("Files\\T6Game.log"); var log = System.IO.File.ReadAllLines("Files\\T6Game.log");

View File

@ -16,6 +16,7 @@ using ApplicationTests.Fixtures;
using System.Threading.Tasks; using System.Threading.Tasks;
using Stats.Helpers; using Stats.Helpers;
using Stats.Dtos; using Stats.Dtos;
using SharedLibraryCore.Configuration;
namespace ApplicationTests namespace ApplicationTests
{ {
@ -73,7 +74,7 @@ namespace ApplicationTests
A.Fake<ITranslationLookup>(), A.Fake<ITranslationLookup>(),
A.Fake<IRConConnectionFactory>(), A.Fake<IGameLogReaderFactory>()); A.Fake<IRConConnectionFactory>(), A.Fake<IGameLogReaderFactory>());
var parser = new BaseEventParser(A.Fake<IParserRegexFactory>(), A.Fake<ILogger>()); var parser = new BaseEventParser(A.Fake<IParserRegexFactory>(), A.Fake<ILogger>(), A.Fake<ApplicationConfiguration>());
parser.Configuration.GuidNumberStyle = System.Globalization.NumberStyles.Integer; parser.Configuration.GuidNumberStyle = System.Globalization.NumberStyles.Integer;
var log = System.IO.File.ReadAllLines("Files\\T6GameStats.log"); var log = System.IO.File.ReadAllLines("Files\\T6GameStats.log");

View File

@ -5,6 +5,7 @@ using System.Threading.Tasks;
using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc; using Microsoft.AspNetCore.Mvc;
using SharedLibraryCore; using SharedLibraryCore;
using SharedLibraryCore.Configuration;
using SharedLibraryCore.Interfaces; using SharedLibraryCore.Interfaces;
using WebfrontCore.ViewModels; using WebfrontCore.ViewModels;
using static SharedLibraryCore.Database.Models.EFClient; using static SharedLibraryCore.Database.Models.EFClient;
@ -13,9 +14,11 @@ namespace WebfrontCore.Controllers
{ {
public class ActionController : BaseController public class ActionController : BaseController
{ {
private readonly ApplicationConfiguration _appConfig;
public ActionController(IManager manager) : base(manager) public ActionController(IManager manager) : base(manager)
{ {
_appConfig = manager.GetApplicationSettings().Configuration();
} }
public IActionResult BanForm() public IActionResult BanForm()
@ -80,8 +83,8 @@ namespace WebfrontCore.Controllers
} }
string command = Duration == 6 ? string command = Duration == 6 ?
$"!ban @{targetId} {Reason}" : $"{_appConfig.CommandPrefix}ban @{targetId} {Reason}" :
$"!tempban @{targetId} {duration} {Reason}"; $"{_appConfig.CommandPrefix}tempban @{targetId} {duration} {Reason}";
var server = Manager.GetServers().First(); var server = Manager.GetServers().First();
@ -120,7 +123,7 @@ namespace WebfrontCore.Controllers
return await Task.FromResult(RedirectToAction("ExecuteAsync", "Console", new return await Task.FromResult(RedirectToAction("ExecuteAsync", "Console", new
{ {
serverId = server.EndPoint, serverId = server.EndPoint,
command = $"!unban @{targetId} {Reason}" command = $"{_appConfig.CommandPrefix}unban @{targetId} {Reason}"
})); }));
} }
@ -189,7 +192,7 @@ namespace WebfrontCore.Controllers
return await Task.FromResult(RedirectToAction("ExecuteAsync", "Console", new return await Task.FromResult(RedirectToAction("ExecuteAsync", "Console", new
{ {
serverId = server.EndPoint, serverId = server.EndPoint,
command = $"!setlevel @{targetId} {level}" command = $"{_appConfig.CommandPrefix}setlevel @{targetId} {level}"
})); }));
} }
@ -256,7 +259,7 @@ namespace WebfrontCore.Controllers
return await Task.FromResult(RedirectToAction("ExecuteAsync", "Console", new return await Task.FromResult(RedirectToAction("ExecuteAsync", "Console", new
{ {
serverId = server.EndPoint, serverId = server.EndPoint,
command = $"!say {message}" command = $"{_appConfig.CommandPrefix}say {message}"
})); }));
} }
@ -294,7 +297,7 @@ namespace WebfrontCore.Controllers
return await Task.FromResult(RedirectToAction("ExecuteAsync", "Console", new return await Task.FromResult(RedirectToAction("ExecuteAsync", "Console", new
{ {
serverId = server.EndPoint, serverId = server.EndPoint,
command = $"!flag @{targetId} {reason}" command = $"{_appConfig.CommandPrefix}flag @{targetId} {reason}"
})); }));
} }
@ -326,7 +329,7 @@ namespace WebfrontCore.Controllers
return await Task.FromResult(RedirectToAction("ExecuteAsync", "Console", new return await Task.FromResult(RedirectToAction("ExecuteAsync", "Console", new
{ {
serverId = server.EndPoint, serverId = server.EndPoint,
command = $"!unflag @{targetId} {reason}" command = $"{_appConfig.CommandPrefix}unflag @{targetId} {reason}"
})); }));
} }
@ -369,7 +372,7 @@ namespace WebfrontCore.Controllers
return await Task.FromResult(RedirectToAction("ExecuteAsync", "Console", new return await Task.FromResult(RedirectToAction("ExecuteAsync", "Console", new
{ {
serverId = client.CurrentServer.EndPoint, serverId = client.CurrentServer.EndPoint,
command = $"!kick {client.ClientNumber} {reason}" command = $"{_appConfig.CommandPrefix}kick {client.ClientNumber} {reason}"
})); }));
} }
} }

View File

@ -1,9 +1,9 @@
using Microsoft.AspNetCore.Mvc; using Microsoft.AspNetCore.Mvc;
using SharedLibraryCore; using SharedLibraryCore;
using SharedLibraryCore.Configuration;
using SharedLibraryCore.Database.Models; using SharedLibraryCore.Database.Models;
using SharedLibraryCore.Dtos; using SharedLibraryCore.Dtos;
using SharedLibraryCore.Interfaces; using SharedLibraryCore.Interfaces;
using System.Collections.Generic;
using System.Linq; using System.Linq;
using System.Threading.Tasks; using System.Threading.Tasks;
@ -11,9 +11,11 @@ namespace WebfrontCore.Controllers
{ {
public class ConsoleController : BaseController public class ConsoleController : BaseController
{ {
private readonly ApplicationConfiguration _appconfig;
public ConsoleController(IManager manager) : base(manager) public ConsoleController(IManager manager) : base(manager)
{ {
_appconfig = manager.GetApplicationSettings().Configuration();
} }
public IActionResult Index() public IActionResult Index()
@ -50,7 +52,8 @@ namespace WebfrontCore.Controllers
var remoteEvent = new GameEvent() var remoteEvent = new GameEvent()
{ {
Type = GameEvent.EventType.Command, Type = GameEvent.EventType.Command,
Data = command, Data = command.StartsWith(_appconfig.CommandPrefix) || command.StartsWith(_appconfig.BroadcastCommandPrefix) ?
command : $"{_appconfig.CommandPrefix}{command}",
Origin = client, Origin = client,
Owner = server, Owner = server,
IsRemote = true IsRemote = true
@ -63,7 +66,7 @@ namespace WebfrontCore.Controllers
{ {
// wait for the event to process // wait for the event to process
var completedEvent = await remoteEvent.WaitAsync(Utilities.DefaultCommandTimeout, server.Manager.CancellationToken); var completedEvent = await remoteEvent.WaitAsync(Utilities.DefaultCommandTimeout, server.Manager.CancellationToken);
if (completedEvent.FailReason == GameEvent.EventFailReason.Timeout) if (completedEvent.FailReason == GameEvent.EventFailReason.Timeout)
{ {
response = new[] response = new[]

View File

@ -61,6 +61,7 @@ namespace WebfrontCore.Controllers
{ {
ViewBag.IsFluid = true; ViewBag.IsFluid = true;
ViewBag.Title = Localization["WEBFRONT_NAV_HELP"]; ViewBag.Title = Localization["WEBFRONT_NAV_HELP"];
ViewBag.CommandPrefix = Manager.GetApplicationSettings().Configuration().CommandPrefix;
// we don't need to the name of the shared library assembly // we don't need to the name of the shared library assembly
var excludedAssembly = typeof(BaseController).Assembly; var excludedAssembly = typeof(BaseController).Assembly;

View File

@ -26,7 +26,7 @@
<td>@command.Alias</td> <td>@command.Alias</td>
<td>@command.Description</td> <td>@command.Description</td>
<td>@command.RequiresTarget</td> <td>@command.RequiresTarget</td>
<td>!@command.Syntax.Split('!')[1]</td> <td>@ViewBag.CommandPrefix@command.Syntax.Split(@ViewBag.CommandPrefix)[1]</td>
<td>@command.Permission.ToLocalizedLevelName()</td> <td>@command.Permission.ToLocalizedLevelName()</td>
</tr> </tr>
} }
@ -62,7 +62,7 @@
</tr> </tr>
<tr> <tr>
<td scope="row" class="bg-primary">@loc["WEBFRONT_HELP_COMMAND_DESC_SYNTAX"]</td> <td scope="row" class="bg-primary">@loc["WEBFRONT_HELP_COMMAND_DESC_SYNTAX"]</td>
<td class="bg-dark text-light">!@command.Syntax.Split('!')[1]</td> <td class="bg-dark text-light">@ViewBag.CommandPrefix@command.Syntax.Split(@ViewBag.CommandPrefix)[1]</td>
</tr> </tr>
<tr> <tr>
<td scope="row" class="bg-primary" style="border-bottom: #222 1px solid;">@loc["WEBFRONT_HELP_COMMAND_DESC_REQUIRED_LEVEL"]</td> <td scope="row" class="bg-primary" style="border-bottom: #222 1px solid;">@loc["WEBFRONT_HELP_COMMAND_DESC_REQUIRED_LEVEL"]</td>

View File

@ -6,10 +6,6 @@
return false; return false;
} }
if (command[0] !== '!') {
$('#console_command_response').text(_localization['WEBFRONT_CONSOLE_COMMAND']).addClass('text-danger');
return false;
}
showLoader(); showLoader();
$.get('/Console/ExecuteAsync', { serverId: serverId, command: command }) $.get('/Console/ExecuteAsync', { serverId: serverId, command: command })
.done(function (response) { .done(function (response) {