diff --git a/Chaskis/ChaskisCore/Handlers/AnyChaskisEvent/AnyChaskisEventHandler.cs b/Chaskis/ChaskisCore/Handlers/AnyChaskisEvent/AnyChaskisEventHandler.cs new file mode 100644 index 00000000..cc321ac7 --- /dev/null +++ b/Chaskis/ChaskisCore/Handlers/AnyChaskisEvent/AnyChaskisEventHandler.cs @@ -0,0 +1,97 @@ +// +// 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 AnyChaskisEventHandlerAction( AnyChaskisEventHandlerArgs args ); + + /// + /// This class will fire for ALL chaskis events that are triggered. + /// + /// Note, this should really only be used when you want to get ALL output + /// from the chaskis event without any filtering. Really only meant to be used for debugging. + /// + public sealed class AnyChaskisEventHandler : IIrcHandler + { + // ---------------- Fields ---------------- + + private readonly AnyChaskisEventHandlerConfig config; + + private static readonly Regex chaskisEventRegex = 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 + { + 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; + } + + AnyChaskisEventHandlerArgs allArgs = new AnyChaskisEventHandlerArgs( args.IrcWriter, args.Line ); + this.LineAction( allArgs ); + } + } +} \ No newline at end of file diff --git a/Chaskis/ChaskisCore/Handlers/AnyChaskisEvent/AnyChaskisEventHandlerArgs.cs b/Chaskis/ChaskisCore/Handlers/AnyChaskisEvent/AnyChaskisEventHandlerArgs.cs new file mode 100644 index 00000000..c4b46aac --- /dev/null +++ b/Chaskis/ChaskisCore/Handlers/AnyChaskisEvent/AnyChaskisEventHandlerArgs.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 AnyChaskisEventHandlerArgs + { + // ---------------- Constructor ---------------- + + public AnyChaskisEventHandlerArgs( IIrcWriter writer, string line ) + { + this.Writer = writer; + this.Line = line; + } + + // ---------------- Properties ---------------- + + /// + /// The Writer to use so we can respond to the chaskis event. + /// + public IIrcWriter Writer { get; private set; } + + /// + /// The raw line that was read from the server. + /// + public string Line { get; private set; } + } +} diff --git a/Chaskis/ChaskisCore/Handlers/AnyChaskisEvent/AnyChaskisEventHandlerConfig.cs b/Chaskis/ChaskisCore/Handlers/AnyChaskisEvent/AnyChaskisEventHandlerConfig.cs new file mode 100644 index 00000000..97da8e5a --- /dev/null +++ b/Chaskis/ChaskisCore/Handlers/AnyChaskisEvent/AnyChaskisEventHandlerConfig.cs @@ -0,0 +1,57 @@ +// +// Copyright Seth Hendrick 2018. +// 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 AnyChaskisEventHandlerConfig + { + // ---------------- Constructor ---------------- + + public AnyChaskisEventHandlerConfig() + { + } + + // ---------------- Properties ---------------- + + /// + /// The action to take when ANY chaskis event. It is up to this action to + /// actually parse the string. + /// + public AnyChaskisEventHandlerAction LineAction { get; set; } + + // ---------------- Functions ---------------- + + public void Validate() + { + bool success = true; + StringBuilder errorString = new StringBuilder(); + errorString.AppendLine( "Errors when validating " + nameof( AnyChaskisEventHandlerConfig ) ); + + 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 AnyChaskisEventHandlerConfig Clone() + { + return (AnyChaskisEventHandlerConfig)this.MemberwiseClone(); + } + } +} diff --git a/Chaskis/ChaskisCore/Handlers/Receive/ReceiveHandler.cs b/Chaskis/ChaskisCore/Handlers/Receive/ReceiveHandler.cs index 045c2a2d..a1d8c051 100644 --- a/Chaskis/ChaskisCore/Handlers/Receive/ReceiveHandler.cs +++ b/Chaskis/ChaskisCore/Handlers/Receive/ReceiveHandler.cs @@ -5,6 +5,7 @@ // http://www.boost.org/LICENSE_1_0.txt) // +using System.Text.RegularExpressions; using SethCS.Exceptions; namespace Chaskis.Core @@ -26,6 +27,11 @@ public sealed class ReceiveHandler : IIrcHandler private readonly ReceiveHandlerConfig config; + private static readonly Regex chaskisEventRegex = new Regex( + @"^ + /// So chaskis events show up in the logs. + /// + private void PrintChaskisEvent( AnyChaskisEventHandlerArgs args ) + { + Console.WriteLine( args.Line ); + } } } diff --git a/Chaskis/UnitTests/CoreTests/Handlers/AnyChaskisEvent/AnyChaskisEventHandlerArgsTests.cs b/Chaskis/UnitTests/CoreTests/Handlers/AnyChaskisEvent/AnyChaskisEventHandlerArgsTests.cs new file mode 100644 index 00000000..f22d52f8 --- /dev/null +++ b/Chaskis/UnitTests/CoreTests/Handlers/AnyChaskisEvent/AnyChaskisEventHandlerArgsTests.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.AnyChaskisEvent +{ + [TestFixture] + public class AnyChaskisEventHandlerArgsTests + { + // ---------------- Tests ---------------- + + /// + /// Ensures the properties get set correctly during construction. + /// + [Test] + public void ConstructorTest() + { + Mock writer = new Mock( MockBehavior.Strict ); + const string line = "line"; + + AnyChaskisEventHandlerArgs uut = new AnyChaskisEventHandlerArgs( + writer.Object, + line + ); + + Assert.AreSame( writer.Object, uut.Writer ); + Assert.AreEqual( line, uut.Line ); + } + } +} diff --git a/Chaskis/UnitTests/CoreTests/Handlers/AnyChaskisEvent/AnyChaskisEventHandlerConfigTests.cs b/Chaskis/UnitTests/CoreTests/Handlers/AnyChaskisEvent/AnyChaskisEventHandlerConfigTests.cs new file mode 100644 index 00000000..417f92a4 --- /dev/null +++ b/Chaskis/UnitTests/CoreTests/Handlers/AnyChaskisEvent/AnyChaskisEventHandlerConfigTests.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.AnyChaskisEvent +{ + [TestFixture] + public class AnyChaskisEventHandlerConfigTests + { + // ---------------- Tests ---------------- + + /// + /// Ensures the Validate() function works as expected. + /// + [Test] + public void ValidateTest() + { + AnyChaskisEventHandlerConfig config = new AnyChaskisEventHandlerConfig(); + + config.LineAction = null; + Assert.Throws( () => config.Validate() ); + + config.LineAction = delegate ( AnyChaskisEventHandlerArgs args ) + { + }; + + Assert.DoesNotThrow( () => config.Validate() ); + } + + [Test] + public void CloneTest() + { + AnyChaskisEventHandlerConfig config1 = new AnyChaskisEventHandlerConfig(); + AnyChaskisEventHandlerConfig clone = config1.Clone(); + + Assert.AreNotSame( config1, clone ); + } + } +} diff --git a/Chaskis/UnitTests/CoreTests/Handlers/AnyChaskisEvent/AnyChaskisEventHandlerTest.cs b/Chaskis/UnitTests/CoreTests/Handlers/AnyChaskisEvent/AnyChaskisEventHandlerTest.cs new file mode 100644 index 00000000..771c61c7 --- /dev/null +++ b/Chaskis/UnitTests/CoreTests/Handlers/AnyChaskisEvent/AnyChaskisEventHandlerTest.cs @@ -0,0 +1,236 @@ +// +// 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; + +namespace Chaskis.UnitTests.CoreTests.Handlers.AnyChaskisEvent +{ + [TestFixture] + public class AnyChaskisEventHandlerTest + { + // -------- Fields -------- + + /// + /// Unit Under Test. + /// + private AnyChaskisEventHandler uut; + + /// + /// Irc Config to use. + /// + private IrcConfig ircConfig; + + /// + /// Mock IRC writer to use. + /// + private Mock ircWriter; + + /// + /// The response AnyChaskisEventd from the event handler (if any). + /// + private AnyChaskisEventHandlerArgs 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; + + AnyChaskisEventHandlerConfig allHandlerConfig = new AnyChaskisEventHandlerConfig + { + LineAction = this.AllFunction + }; + this.uut = new AnyChaskisEventHandler( allHandlerConfig ); + } + + // -------- Tests -------- + + /// + /// Ensures that if a bad config is passed in, we throw an exception. + /// + [Test] + public void InvalidConfigTest() + { + Assert.Throws( + () => new AnyChaskisEventHandler( new AnyChaskisEventHandlerConfig() ) + ); + } + + /// + /// 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 AnyChaskisEventHandler( 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 ) ); + this.CheckResponse( eventString ); + } + + [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 ) ); + this.CheckResponse( eventString ); + } + + // -------- Test Helpers -------- + + /// + /// The function that is called + /// + /// The writer that can be written to. + /// The response from the server. + private void AllFunction( AnyChaskisEventHandlerArgs 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 438eaf24..7a503348 100644 --- a/Chaskis/UnitTests/CoreTests/Handlers/Receive/ReceiveHandlerTest.cs +++ b/Chaskis/UnitTests/CoreTests/Handlers/Receive/ReceiveHandlerTest.cs @@ -91,7 +91,7 @@ public void ConstructionTest() public void ArgumentNullTest() { Assert.Throws( () => - new PartHandler( null ) + new ReceiveHandler( null ) ); Assert.Throws( () => @@ -163,6 +163,25 @@ public void PingAppears() this.CheckResponse( ircString ); } + [Test] + public void ChaskisEventAppears() + { + 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 Helpers -------- ///