diff --git a/Chaskis/ChaskisCore/Chaskis.Core.csproj b/Chaskis/ChaskisCore/Chaskis.Core.csproj index 15e8457..b6f6288 100644 --- a/Chaskis/ChaskisCore/Chaskis.Core.csproj +++ b/Chaskis/ChaskisCore/Chaskis.Core.csproj @@ -24,6 +24,7 @@ + diff --git a/Chaskis/ChaskisCore/Handlers/AnyChaskisEvent/AnyChaskisEventHandler.cs b/Chaskis/ChaskisCore/Handlers/AnyChaskisEvent/AnyChaskisEventHandler.cs index cc321ac..e443c8d 100644 --- a/Chaskis/ChaskisCore/Handlers/AnyChaskisEvent/AnyChaskisEventHandler.cs +++ b/Chaskis/ChaskisCore/Handlers/AnyChaskisEvent/AnyChaskisEventHandler.cs @@ -24,7 +24,7 @@ public sealed class AnyChaskisEventHandler : IIrcHandler private readonly AnyChaskisEventHandlerConfig config; - private static readonly Regex chaskisEventRegex = new Regex( + internal static readonly Regex Regex = new Regex( @"^ - /// The action to take when ANY message appears from IRC (JOIN, PART, PRIVMSG, PING, etc). - /// As far as the passed in IrcResponse to the action goes, the channel and remote user - /// will be String.Empty, since this class does no parsing of the IRC message. - /// It just grabs the line from the IRC channel and passes it into the AllAction - /// with no parsing. It is up to the AllAction to parse the channel and user - /// name if they so desire. - /// public AnyChaskisEventHandlerAction LineAction { get @@ -85,7 +77,7 @@ public void HandleEvent( HandlerArgs args ) { ArgumentChecker.IsNotNull( args, nameof( args ) ); - if( chaskisEventRegex.IsMatch( args.Line ) == false ) + if( Regex.IsMatch( args.Line ) == false ) { return; } diff --git a/Chaskis/ChaskisCore/Handlers/AnyChaskisEvent/AnyChaskisEventHandlerArgs.cs b/Chaskis/ChaskisCore/Handlers/AnyChaskisEvent/AnyChaskisEventHandlerArgs.cs index c4b46aa..2e55b1d 100644 --- a/Chaskis/ChaskisCore/Handlers/AnyChaskisEvent/AnyChaskisEventHandlerArgs.cs +++ b/Chaskis/ChaskisCore/Handlers/AnyChaskisEvent/AnyChaskisEventHandlerArgs.cs @@ -28,7 +28,7 @@ public AnyChaskisEventHandlerArgs( IIrcWriter writer, string line ) public IIrcWriter Writer { get; private set; } /// - /// The raw line that was read from the server. + /// The raw line that was received. /// public string Line { get; private set; } } diff --git a/Chaskis/ChaskisCore/Handlers/AnyChaskisEvent/AnyChaskisEventHandlerConfig.cs b/Chaskis/ChaskisCore/Handlers/AnyChaskisEvent/AnyChaskisEventHandlerConfig.cs index 97da8e5..e35e4c8 100644 --- a/Chaskis/ChaskisCore/Handlers/AnyChaskisEvent/AnyChaskisEventHandlerConfig.cs +++ b/Chaskis/ChaskisCore/Handlers/AnyChaskisEvent/AnyChaskisEventHandlerConfig.cs @@ -24,7 +24,7 @@ public AnyChaskisEventHandlerConfig() // ---------------- Properties ---------------- /// - /// The action to take when ANY chaskis event. It is up to this action to + /// The action to take when ANY chaskis event occurs. It is up to this action to /// actually parse the string. /// public AnyChaskisEventHandlerAction LineAction { get; set; } diff --git a/Chaskis/ChaskisCore/Handlers/AnyInterPluginEvent/AnyInterPluginEventHandler.cs b/Chaskis/ChaskisCore/Handlers/AnyInterPluginEvent/AnyInterPluginEventHandler.cs new file mode 100644 index 0000000..4bbef06 --- /dev/null +++ b/Chaskis/ChaskisCore/Handlers/AnyInterPluginEvent/AnyInterPluginEventHandler.cs @@ -0,0 +1,89 @@ +// +// Copyright Seth Hendrick 2016-2020. +// Distributed under the Boost Software License, Version 1.0. +// (See accompanying file LICENSE_1_0.txt or copy at +// http://www.boost.org/LICENSE_1_0.txt) +// + +using System.Text.RegularExpressions; +using SethCS.Exceptions; + +namespace Chaskis.Core +{ + public delegate void AnyInterPluginEventHandlerAction( AnyInterPluginEventHandlerArgs args ); + + /// + /// This class will fire for ALL inter-plugin events that are triggered. + /// + /// Note, this should really only be used when you want to get ALL output + /// from the inter-plugin event without any filtering. Really only meant to be used for debugging. + /// + public sealed class AnyInterPluginEventHandler : IIrcHandler + { + // ---------------- Fields ---------------- + + private readonly AnyInterPluginEventHandlerConfig config; + + private static readonly Regex chaskisEventRegex = new Regex( + $@"^\<{InterPluginEventExtensions.XmlRootName}.+", + RegexOptions.Compiled | RegexOptions.ExplicitCapture + ); + + // ---------------- Constructor ---------------- + + public AnyInterPluginEventHandler( AnyInterPluginEventHandlerConfig allConfig ) + { + ArgumentChecker.IsNotNull( allConfig, nameof( allConfig ) ); + + allConfig.Validate(); + + this.config = allConfig.Clone(); + this.KeepHandling = true; + } + + // ---------------- Properties ---------------- + + public AnyInterPluginEventHandlerAction LineAction + { + get + { + return this.config.LineAction; + } + } + + /// + /// Whether or not the handler should keep handling or not. + /// Set to true to keep handling the event when it appears in the chat. + /// Set to false so when the current IRC message is finished processing being, + /// it leaves the event queue and never + /// happens again. Useful for events that only need to happen once. + /// + /// This is a public get/set. Either classes outside of the handler can + /// tell the handler to cancel the event, or it can cancel itself. + /// + /// Note: when this is set to false, there must be one more IRC message that appears + /// before it is removed from the queue. + /// + /// Defaulted to true. + /// + public bool KeepHandling { get; set; } + + // ---------------- Functions ---------------- + + /// + /// Handles the event. + /// + public void HandleEvent( HandlerArgs args ) + { + ArgumentChecker.IsNotNull( args, nameof( args ) ); + + if( chaskisEventRegex.IsMatch( args.Line ) == false ) + { + return; + } + + AnyInterPluginEventHandlerArgs allArgs = new AnyInterPluginEventHandlerArgs( args.IrcWriter, args.Line ); + this.LineAction( allArgs ); + } + } +} \ No newline at end of file diff --git a/Chaskis/ChaskisCore/Handlers/AnyInterPluginEvent/AnyInterPluginEventHandlerArgs.cs b/Chaskis/ChaskisCore/Handlers/AnyInterPluginEvent/AnyInterPluginEventHandlerArgs.cs new file mode 100644 index 0000000..2512be0 --- /dev/null +++ b/Chaskis/ChaskisCore/Handlers/AnyInterPluginEvent/AnyInterPluginEventHandlerArgs.cs @@ -0,0 +1,35 @@ +// +// Copyright Seth Hendrick 2020. +// Distributed under the Boost Software License, Version 1.0. +// (See accompanying file LICENSE_1_0.txt or copy at +// http://www.boost.org/LICENSE_1_0.txt) +// + +namespace Chaskis.Core +{ + /// + /// Arguments that are passed in when is triggered. + /// + public class AnyInterPluginEventHandlerArgs + { + // ---------------- Constructor ---------------- + + public AnyInterPluginEventHandlerArgs( IIrcWriter writer, string line ) + { + this.Writer = writer; + this.Line = line; + } + + // ---------------- Properties ---------------- + + /// + /// The Writer to use so we can respond to the event. + /// + public IIrcWriter Writer { get; private set; } + + /// + /// The raw line that was received. + /// + public string Line { get; private set; } + } +} diff --git a/Chaskis/ChaskisCore/Handlers/AnyInterPluginEvent/AnyInterPluginEventHandlerConfig.cs b/Chaskis/ChaskisCore/Handlers/AnyInterPluginEvent/AnyInterPluginEventHandlerConfig.cs new file mode 100644 index 0000000..bd72a28 --- /dev/null +++ b/Chaskis/ChaskisCore/Handlers/AnyInterPluginEvent/AnyInterPluginEventHandlerConfig.cs @@ -0,0 +1,57 @@ +// +// Copyright Seth Hendrick 2020. +// Distributed under the Boost Software License, Version 1.0. +// (See accompanying file LICENSE_1_0.txt or copy at +// http://www.boost.org/LICENSE_1_0.txt) +// + +using System.Text; +using SethCS.Exceptions; + +namespace Chaskis.Core +{ + /// + /// Configuration used for + /// + public class AnyInterPluginEventHandlerConfig + { + // ---------------- Constructor ---------------- + + public AnyInterPluginEventHandlerConfig() + { + } + + // ---------------- Properties ---------------- + + /// + /// The action to take for ANY inter-plugin event. It is up to this action to + /// actually parse the string. + /// + public AnyInterPluginEventHandlerAction LineAction { get; set; } + + // ---------------- Functions ---------------- + + public void Validate() + { + bool success = true; + StringBuilder errorString = new StringBuilder(); + errorString.AppendLine( "Errors when validating " + nameof( AnyInterPluginEventHandlerConfig ) ); + + if( this.LineAction == null ) + { + success = false; + errorString.AppendLine( "\t- " + nameof( this.LineAction ) + " can not be null" ); + } + + if( success == false ) + { + throw new ValidationException( errorString.ToString() ); + } + } + + public AnyInterPluginEventHandlerConfig Clone() + { + return (AnyInterPluginEventHandlerConfig)this.MemberwiseClone(); + } + } +} diff --git a/Chaskis/ChaskisCore/Handlers/InterPluginEventHandler.cs b/Chaskis/ChaskisCore/Handlers/InterPluginEventHandler.cs index 3d4ebf0..8156e3e 100644 --- a/Chaskis/ChaskisCore/Handlers/InterPluginEventHandler.cs +++ b/Chaskis/ChaskisCore/Handlers/InterPluginEventHandler.cs @@ -25,7 +25,7 @@ public class InterPluginEventHandler : IIrcHandler private static string interPluginHelper = $@"^\<{InterPluginEventExtensions.XmlRootName}.+\$"; - private static readonly Regex chaskisRegex = new Regex( + internal static readonly Regex Regex = new Regex( interPluginHelper, RegexOptions.Compiled | RegexOptions.ExplicitCapture ); @@ -91,7 +91,7 @@ Action lineAction /// public void HandleEvent( HandlerArgs args ) { - if( chaskisRegex.IsMatch( args.Line ) == false ) + if( Regex.IsMatch( args.Line ) == false ) { return; } diff --git a/Chaskis/ChaskisCore/Handlers/Receive/ReceiveHandler.cs b/Chaskis/ChaskisCore/Handlers/Receive/ReceiveHandler.cs index a1d8c05..fc22c33 100644 --- a/Chaskis/ChaskisCore/Handlers/Receive/ReceiveHandler.cs +++ b/Chaskis/ChaskisCore/Handlers/Receive/ReceiveHandler.cs @@ -27,11 +27,6 @@ public sealed class ReceiveHandler : IIrcHandler private readonly ReceiveHandlerConfig config; - private static readonly Regex chaskisEventRegex = new Regex( - @"^ + { + ["arq1"] = "1" + }; + + var passArgs = new Dictionary + { + ["pass1"] = "2" + }; + + Core.InterPluginEvent e = new Core.InterPluginEvent( + "plugin1", + "plugin2", + args, + passArgs + ); + + this.uut.HandleEvent( this.ConstructArgs( e.ToXml() ) ); + Assert.IsNull( this.responseReceived ); + } + // -------- Test Helpers -------- /// diff --git a/Chaskis/UnitTests/CoreTests/Handlers/AnyInterPluginEvent/AnyInterPluginEventHandlerArgsTests.cs b/Chaskis/UnitTests/CoreTests/Handlers/AnyInterPluginEvent/AnyInterPluginEventHandlerArgsTests.cs new file mode 100644 index 0000000..2327bf0 --- /dev/null +++ b/Chaskis/UnitTests/CoreTests/Handlers/AnyInterPluginEvent/AnyInterPluginEventHandlerArgsTests.cs @@ -0,0 +1,37 @@ +// +// Copyright Seth Hendrick 2020. +// Distributed under the Boost Software License, Version 1.0. +// (See accompanying file LICENSE_1_0.txt or copy at +// http://www.boost.org/LICENSE_1_0.txt) +// + +using Chaskis.Core; +using Moq; +using NUnit.Framework; + +namespace Chaskis.UnitTests.CoreTests.Handlers.AnyInterPluginEvent +{ + [TestFixture] + public class AnyInterPluginEventHandlerArgsTests + { + // ---------------- Tests ---------------- + + /// + /// Ensures the properties get set correctly during construction. + /// + [Test] + public void ConstructorTest() + { + Mock writer = new Mock( MockBehavior.Strict ); + const string line = "line"; + + AnyInterPluginEventHandlerArgs uut = new AnyInterPluginEventHandlerArgs( + writer.Object, + line + ); + + Assert.AreSame( writer.Object, uut.Writer ); + Assert.AreEqual( line, uut.Line ); + } + } +} diff --git a/Chaskis/UnitTests/CoreTests/Handlers/AnyInterPluginEvent/AnyInterPluginEventHandlerConfigTests.cs b/Chaskis/UnitTests/CoreTests/Handlers/AnyInterPluginEvent/AnyInterPluginEventHandlerConfigTests.cs new file mode 100644 index 0000000..b540228 --- /dev/null +++ b/Chaskis/UnitTests/CoreTests/Handlers/AnyInterPluginEvent/AnyInterPluginEventHandlerConfigTests.cs @@ -0,0 +1,46 @@ +// +// Copyright Seth Hendrick 2020. +// Distributed under the Boost Software License, Version 1.0. +// (See accompanying file LICENSE_1_0.txt or copy at +// http://www.boost.org/LICENSE_1_0.txt) +// + +using Chaskis.Core; +using NUnit.Framework; +using SethCS.Exceptions; + +namespace Chaskis.UnitTests.CoreTests.Handlers.AnyInterPluginEvent +{ + [TestFixture] + public class AnyInterPluginEventHandlerConfigTests + { + // ---------------- Tests ---------------- + + /// + /// Ensures the Validate() function works as expected. + /// + [Test] + public void ValidateTest() + { + AnyInterPluginEventHandlerConfig config = new AnyInterPluginEventHandlerConfig(); + + config.LineAction = null; + Assert.Throws( () => config.Validate() ); + + config.LineAction = delegate ( AnyInterPluginEventHandlerArgs args ) + { + }; + + Assert.DoesNotThrow( () => config.Validate() ); + } + + [Test] + public void CloneTest() + { + AnyInterPluginEventHandlerConfig config1 = new AnyInterPluginEventHandlerConfig(); + AnyInterPluginEventHandlerConfig clone = config1.Clone(); + + Assert.AreNotSame( config1, clone ); + } + } +} diff --git a/Chaskis/UnitTests/CoreTests/Handlers/AnyInterPluginEvent/AnyInterPluginEventHandlerTest.cs b/Chaskis/UnitTests/CoreTests/Handlers/AnyInterPluginEvent/AnyInterPluginEventHandlerTest.cs new file mode 100644 index 0000000..100dc88 --- /dev/null +++ b/Chaskis/UnitTests/CoreTests/Handlers/AnyInterPluginEvent/AnyInterPluginEventHandlerTest.cs @@ -0,0 +1,261 @@ +// +// Copyright Seth Hendrick 2020. +// Distributed under the Boost Software License, Version 1.0. +// (See accompanying file LICENSE_1_0.txt or copy at +// http://www.boost.org/LICENSE_1_0.txt) +// + +using System; +using Chaskis.UnitTests.Common; +using Chaskis.Core; +using Moq; +using NUnit.Framework; +using SethCS.Exceptions; +using System.Collections.Generic; + +namespace Chaskis.UnitTests.CoreTests.Handlers.AnyInterPluginEvent +{ + [TestFixture] + public class AnyInterPluginEventHandlerTest + { + // -------- Fields -------- + + /// + /// Unit Under Test. + /// + private AnyInterPluginEventHandler uut; + + /// + /// Irc Config to use. + /// + private IrcConfig ircConfig; + + /// + /// Mock IRC writer to use. + /// + private Mock ircWriter; + + /// + /// The response AnyInterPluginEventd from the event handler (if any). + /// + private AnyInterPluginEventHandlerArgs responseReceived; + + /// + /// A user. + /// + private const string remoteUser = "remoteuser"; + + // -------- Setup / Teardown -------- + + [SetUp] + public void TestSetup() + { + this.ircConfig = TestHelpers.GetTestIrcConfig(); + this.ircWriter = new Mock( MockBehavior.Strict ); + this.responseReceived = null; + + AnyInterPluginEventHandlerConfig config = new AnyInterPluginEventHandlerConfig + { + LineAction = this.Function + }; + this.uut = new AnyInterPluginEventHandler( config ); + } + + // -------- Tests -------- + + /// + /// Ensures that if a bad config is passed in, we throw an exception. + /// + [Test] + public void InvalidConfigTest() + { + Assert.Throws( + () => new AnyInterPluginEventHandler( new AnyInterPluginEventHandlerConfig() ) + ); + } + + /// + /// Ensures that the class is created correctly. + /// + [Test] + public void ConstructionTest() + { + // Keep Handling should be true by default. + Assert.IsTrue( this.uut.KeepHandling ); + } + + /// + /// Ensurs everything that needs to throw argument null + /// exceptions does. + /// + [Test] + public void ArgumentNullTest() + { + Assert.Throws( () => + new AnyInterPluginEventHandler( null ) + ); + + Assert.Throws( () => + this.uut.HandleEvent( null ) + ); + + Assert.IsNull( this.responseReceived ); // Ensure handler didn't get called. + } + + /// + /// Ensures that if the bot parts, the event is not fired. + /// + [Test] + public void BotParts() + { + string ircString = TestHelpers.ConstructIrcString( + this.ircConfig.Nick, + PartHandler.IrcCommand, + this.ircConfig.Channels[0], + string.Empty + ); + + this.uut.HandleEvent( this.ConstructArgs( ircString ) ); + Assert.IsNull( this.responseReceived ); + } + + /// + /// Ensures that if a PRIMSG appears, the event is not fired. + /// + [Test] + public void MessageCommandAppears() + { + string ircString = TestHelpers.ConstructIrcString( + remoteUser, + PrivateMessageHelper.IrcCommand, + this.ircConfig.Channels[0], + "A message" + ); + + this.uut.HandleEvent( this.ConstructArgs( ircString ) ); + Assert.IsNull( this.responseReceived ); + } + + /// + /// Ensures that if a JOIN appears, the event is not fired. + /// + [Test] + public void JoinCommandAppears() + { + string ircString = TestHelpers.ConstructIrcString( + remoteUser, + JoinHandler.IrcCommand, + this.ircConfig.Channels[0], + string.Empty + ); + + this.uut.HandleEvent( this.ConstructArgs( ircString ) ); + Assert.IsNull( this.responseReceived ); + } + + /// + /// Ensures that if a PING appears, the event is not fired. + /// + [Test] + public void PingAppears() + { + string ircString = TestHelpers.ConstructPingString( "12345" ); + this.uut.HandleEvent( this.ConstructArgs( ircString ) ); + Assert.IsNull( this.responseReceived ); + } + + [Test] + public void SendNoticeAppears() + { + SendNoticeEventArgs e = new SendNoticeEventArgs + { + Protocol = ChaskisEventProtocol.IRC, + Server = "irc.somewhere.net", + Writer = this.ircWriter.Object, + + ChannelOrUser = "#channel", + Message = "A message" + }; + + string eventString = e.ToXml(); + this.uut.HandleEvent( this.ConstructArgs( eventString ) ); + Assert.IsNull( this.responseReceived ); + } + + [Test] + public void SendJoinAppears() + { + SendJoinEventArgs e = new SendJoinEventArgs + { + Protocol = ChaskisEventProtocol.IRC, + Server = "irc.somewhere.net", + Writer = this.ircWriter.Object + }; + + string eventString = e.ToXml(); + this.uut.HandleEvent( this.ConstructArgs( eventString ) ); + Assert.IsNull( this.responseReceived ); + } + + [Test] + public void SendInterPluginEvent() + { + var args = new Dictionary + { + ["arq1"] = "1" + }; + + var passArgs = new Dictionary + { + ["pass1"] = "2" + }; + + Core.InterPluginEvent e = new Core.InterPluginEvent( + "plugin1", + "plugin2", + args, + passArgs + ); + + this.uut.HandleEvent( this.ConstructArgs( e.ToXml() ) ); + CheckResponse( e.ToXml() ); + } + + // -------- Test Helpers -------- + + /// + /// The function that is called + /// + /// The writer that can be written to. + /// The response from the server. + private void Function( AnyInterPluginEventHandlerArgs args ) + { + Assert.AreSame( this.ircWriter.Object, args.Writer ); + this.responseReceived = args; + } + + /// + /// Ensures the bot responds accordingly. + /// + /// The raw string we expect. + private void CheckResponse( string rawString ) + { + Assert.IsNotNull( this.responseReceived ); + + // Ensure its the raw string. + Assert.AreEqual( rawString, this.responseReceived.Line ); + } + + private HandlerArgs ConstructArgs( string line ) + { + HandlerArgs args = new HandlerArgs + { + Line = line, + IrcWriter = this.ircWriter.Object, + IrcConfig = this.ircConfig + }; + + return args; + } + } +} \ No newline at end of file diff --git a/Chaskis/UnitTests/CoreTests/Handlers/Receive/ReceiveHandlerTest.cs b/Chaskis/UnitTests/CoreTests/Handlers/Receive/ReceiveHandlerTest.cs index 7a50334..7b68e16 100644 --- a/Chaskis/UnitTests/CoreTests/Handlers/Receive/ReceiveHandlerTest.cs +++ b/Chaskis/UnitTests/CoreTests/Handlers/Receive/ReceiveHandlerTest.cs @@ -11,6 +11,7 @@ using Moq; using NUnit.Framework; using SethCS.Exceptions; +using System.Collections.Generic; namespace Chaskis.UnitTests.CoreTests.Handlers.Receive { @@ -182,6 +183,30 @@ public void ChaskisEventAppears() Assert.IsNull( this.responseReceived ); } + [Test] + public void SendInterPluginEvent() + { + var args = new Dictionary + { + ["arq1"] = "1" + }; + + var passArgs = new Dictionary + { + ["pass1"] = "2" + }; + + Core.InterPluginEvent e = new Core.InterPluginEvent( + "plugin1", + "plugin2", + args, + passArgs + ); + + this.uut.HandleEvent( this.ConstructArgs( e.ToXml() ) ); + Assert.IsNull( this.responseReceived ); + } + // -------- Test Helpers -------- ///