diff --git a/IrcStates/ISupport.cs b/IrcStates/ISupport.cs index bfee9ee..b9ad502 100644 --- a/IrcStates/ISupport.cs +++ b/IrcStates/ISupport.cs @@ -1,8 +1,89 @@ // ReSharper disable InconsistentNaming +using System; +using System.Collections.Generic; +using System.Globalization; + namespace IrcStates { public class ISupport { + public ISupport() + { + Raw = new Dictionary(); + Modes = 3; + CaseMapping = Casemap.CaseMapping.Rfc1459; + // TODO: add remaining defaults, change properties to normal members as needed + } + + public Dictionary Raw { get; set; } + public string Network { get; set; } + public ISupportChanModes ChanModes { get; set; } + public ISupportPrefix Prefix { get; set; } + public int? Modes { get; set; } + public Casemap.CaseMapping CaseMapping { get; set; } + public List ChanTypes { get; set; } + public List StatusMsg { get; set; } + public string CallerId { get; set; } + public string Excepts { get; set; } + public string Invex { get; set; } + public int? Monitor { get; set; } + public int? Watch { get; set; } + public bool Whox { get; set; } + + public void Parse(IList tokens) + { + foreach (var token in tokens) + { + var split = token.Split('=', 2); + var key = split[0]; + var value = split[1]; + + if (split.Length > 1) Raw[key] = value; + + switch (split[0]) + { + case "NETWORK": + Network = value; + break; + case "CHANMODES": + ChanModes = new ISupportChanModes(value); + break; + case "PREFIX": + Prefix = new ISupportPrefix(value); + break; + case "STATUSMSG": + StatusMsg = new List {value}; + break; + case "MODES": + Modes = int.Parse(value, NumberStyles.Integer, CultureInfo.InvariantCulture); + break; + case "MONITOR": + Monitor = int.Parse(value, NumberStyles.Integer, CultureInfo.InvariantCulture); + break; + case "WATCH": + Watch = int.Parse(value, NumberStyles.Integer, CultureInfo.InvariantCulture); + break; + case "CASEMAPPING": + if (Enum.TryParse(value, true, out Casemap.CaseMapping caseMapping)) CaseMapping = caseMapping; + break; + case "CHANTYPES": + ChanTypes = new List {value}; + break; + case "CALLERID": + CallerId = string.IsNullOrEmpty(value) ? value : "g"; + break; + case "EXCEPTS": + Excepts = string.IsNullOrEmpty(value) ? value : "e"; + break; + case "INVEX": + Invex = string.IsNullOrEmpty(value) ? value : "I"; + break; + case "WHOX": + Whox = true; + break; + } + } + } } } diff --git a/IrcStates/ISupportChanModes.cs b/IrcStates/ISupportChanModes.cs new file mode 100644 index 0000000..9bf565c --- /dev/null +++ b/IrcStates/ISupportChanModes.cs @@ -0,0 +1,23 @@ +using System.Collections.Generic; + +namespace IrcStates +{ + public class ISupportChanModes + { + public ISupportChanModes(string splitVal) + { + if (splitVal == null) return; + + var split = splitVal.Split(',', 4); + ListModes = new List {split[0]}; + SettingBModes = new List {split[1]}; + SettingCModes = new List {split[2]}; + SettingDModes = new List {split[3]}; + } + + public List ListModes { get; set; } + public List SettingBModes { get; set; } + public List SettingCModes { get; set; } + public List SettingDModes { get; set; } + } +} diff --git a/IrcStates/ISupportPrefix.cs b/IrcStates/ISupportPrefix.cs new file mode 100644 index 0000000..94668e3 --- /dev/null +++ b/IrcStates/ISupportPrefix.cs @@ -0,0 +1,27 @@ +using System.Collections.Generic; + +namespace IrcStates +{ + public class ISupportPrefix + { + public ISupportPrefix(string splitVal) + { + var split = splitVal.Substring(1).Split(')', 2); + Modes = new List {split[0]}; + Prefixes = new List {split[1]}; + } + + public List Modes { get; set; } + public List Prefixes { get; set; } + + public string FromMode(string mode) + { + return Modes.Contains(mode) ? Modes[Modes.IndexOf(mode)] : null; + } + + public string FromPrefix(string prefix) + { + return Prefixes.Contains(prefix) ? Prefixes[Prefixes.IndexOf(prefix)] : null; + } + } +} diff --git a/IrcStates/README.md b/IrcStates/README.md new file mode 100644 index 0000000..60fcc44 --- /dev/null +++ b/IrcStates/README.md @@ -0,0 +1,5 @@ +# IrcStates + +port of [jesopo/ircstates](https://github.com/jesopo/ircstates) + +bare bones irc client state diff --git a/IrcStates/Server.cs b/IrcStates/Server.cs index 9371a2b..7199668 100644 --- a/IrcStates/Server.cs +++ b/IrcStates/Server.cs @@ -7,6 +7,7 @@ namespace IrcStates { public class Server { + public const string WhoType = "525"; // randomly generated private readonly StatefulDecoder _decoder; public Dictionary>> LineHandlers; @@ -81,5 +82,10 @@ namespace IrcStates return ret; } + + public string CaseFold(string str) + { + return Casemap.CaseFold(ISupport.CaseMapping, str); + } } } diff --git a/IrcStates/Tests/ISupport.cs b/IrcStates/Tests/ISupport.cs index f85f087..81b3cff 100644 --- a/IrcStates/Tests/ISupport.cs +++ b/IrcStates/Tests/ISupport.cs @@ -1,4 +1,6 @@ -using Microsoft.VisualStudio.TestTools.UnitTesting; +using System.Collections.Generic; +using IrcTokens; +using Microsoft.VisualStudio.TestTools.UnitTesting; // ReSharper disable InconsistentNaming @@ -7,5 +9,202 @@ namespace IrcStates.Tests [TestClass] public class ISupport { + private Server _server; + + [TestInitialize] + public void TestInitialize() + { + _server = new Server("test"); + _server.ParseTokens(new Line("001 nickname")); + } + + [TestMethod] + public void ChanModes() + { + CollectionAssert.AreEqual(new List {"b"}, _server.ISupport.ChanModes.ListModes); + CollectionAssert.AreEqual(new List {"k"}, _server.ISupport.ChanModes.SettingBModes); + CollectionAssert.AreEqual(new List {"l"}, _server.ISupport.ChanModes.SettingCModes); + CollectionAssert.AreEqual(new List + { + "i", + "m", + "n", + "p", + "s", + "t" + }, _server.ISupport.ChanModes.SettingDModes); + + _server.ParseTokens(new Line("005 * CHANMODES=a,b,c,d *")); + + CollectionAssert.AreEqual(new List {"a"}, _server.ISupport.ChanModes.ListModes); + CollectionAssert.AreEqual(new List {"b"}, _server.ISupport.ChanModes.SettingBModes); + CollectionAssert.AreEqual(new List {"c"}, _server.ISupport.ChanModes.SettingCModes); + CollectionAssert.AreEqual(new List {"d"}, _server.ISupport.ChanModes.SettingDModes); + } + + [TestMethod] + public void Prefix() + { + CollectionAssert.AreEqual(new List {"o", "v"}, _server.ISupport.Prefix.Modes); + CollectionAssert.AreEqual(new List {"@", "+"}, _server.ISupport.Prefix.Prefixes); + + Assert.AreEqual("@", _server.ISupport.Prefix.FromMode("o")); + Assert.IsNull(_server.ISupport.Prefix.FromMode("a")); + Assert.AreEqual("o", _server.ISupport.Prefix.FromPrefix("@")); + Assert.IsNull(_server.ISupport.Prefix.FromPrefix("&")); + + _server.ParseTokens(new Line("005 * PREFIX=(qaohv)~&#%+ *")); + CollectionAssert.AreEqual(new List + { + "q", + "a", + "o", + "h", + "v" + }, _server.ISupport.Prefix.Modes); + CollectionAssert.AreEqual(new List + { + "~", + "&", + "@", + "%", + "+" + }, _server.ISupport.Prefix.Prefixes); + Assert.AreEqual("&", _server.ISupport.Prefix.FromMode("a")); + Assert.AreEqual("a", _server.ISupport.Prefix.FromPrefix("&")); + } + + [TestMethod] + public void ChanTypes() + { + CollectionAssert.AreEqual(new List {"#"}, _server.ISupport.ChanTypes); + _server.ParseTokens(new Line("005 * CHANTYPES=#& *")); + CollectionAssert.AreEqual(new List {"#", "&"}, _server.ISupport.ChanTypes); + } + + [TestMethod] + public void Modes() + { + Assert.AreEqual(3, _server.ISupport.Modes); + + _server.ParseTokens(new Line("005 * MODES *")); + Assert.AreEqual(-1, _server.ISupport.Modes); + + _server.ParseTokens(new Line("005 * MODES=5 *")); + Assert.AreEqual(5, _server.ISupport.Modes); + } + + [TestMethod] + public void Rfc1459() + { + Assert.AreEqual(IrcStates.Casemap.CaseMapping.Rfc1459, _server.ISupport.CaseMapping); + _server.ParseTokens(new Line("005 * CASEMAPPING=rfc1459 *")); + Assert.AreEqual(IrcStates.Casemap.CaseMapping.Rfc1459, _server.ISupport.CaseMapping); + var lower = _server.CaseFold(@"ÀTEST[]~\"); + Assert.AreEqual("Àtest{}^|", lower); + } + + [TestMethod] + public void Ascii() + { + _server.ParseTokens(new Line("005 * CASEMAPPING=ascii *")); + Assert.AreEqual(IrcStates.Casemap.CaseMapping.Ascii, _server.ISupport.CaseMapping); + var lower = _server.CaseFold(@"ÀTEST[]~\"); + Assert.AreEqual(@"Àtest[]~\", lower); + } + + [TestMethod] + public void FallbackToRfc1459() + { + _server.ParseTokens(new Line("005 * CASEMAPPING=nonexistent *")); + Assert.AreEqual(IrcStates.Casemap.CaseMapping.Rfc1459, _server.ISupport.CaseMapping); + var lower = _server.CaseFold(@"ÀTEST[]~\"); + Assert.AreEqual("Àtest{}^|", lower); + } + + [TestMethod] + public void Network() + { + Assert.IsNull(_server.ISupport.Network); + _server.ParseTokens(new Line("005 * NETWORK=testnet *")); + Assert.AreEqual("testnet", _server.ISupport.Network); + } + + [TestMethod] + public void StatusMsg() + { + CollectionAssert.AreEqual(new List(), _server.ISupport.StatusMsg); + _server.ParseTokens(new Line("005 * STATUSMSG=&@ *")); + CollectionAssert.AreEqual(new List {"&", "@"}, _server.ISupport.StatusMsg); + } + + [TestMethod] + public void CallerId() + { + Assert.IsNull(_server.ISupport.CallerId); + + _server.ParseTokens(new Line("005 * CALLERID=U *")); + Assert.AreEqual("U", _server.ISupport.CallerId); + + _server.ParseTokens(new Line("005 * CALLERID *")); + Assert.AreEqual("g", _server.ISupport.CallerId); + } + + [TestMethod] + public void Excepts() + { + Assert.IsNull(_server.ISupport.Excepts); + + _server.ParseTokens(new Line("005 * EXCEPTS=U *")); + Assert.AreEqual("U", _server.ISupport.Excepts); + + _server.ParseTokens(new Line("005 * EXCEPTS *")); + Assert.AreEqual("e", _server.ISupport.Excepts); + } + + [TestMethod] + public void Invex() + { + Assert.IsNull(_server.ISupport.Invex); + + _server.ParseTokens(new Line("005 * INVEX=U *")); + Assert.AreEqual("U", _server.ISupport.Invex); + + _server.ParseTokens(new Line("005 * INVEX *")); + Assert.AreEqual("I", _server.ISupport.Invex); + } + + [TestMethod] + public void Whox() + { + Assert.IsFalse(_server.ISupport.Whox); + + _server.ParseTokens(new Line("005 * WHOX *")); + Assert.IsTrue(_server.ISupport.Whox); + } + + [TestMethod] + public void Monitor() + { + Assert.IsNull(_server.ISupport.Monitor); + + _server.ParseTokens(new Line("005 * MONITOR=123 *")); + Assert.AreEqual(123, _server.ISupport.Monitor); + + _server.ParseTokens(new Line("005 * MONITOR *")); + Assert.AreEqual(-1, _server.ISupport.Monitor); + } + + [TestMethod] + public void Watch() + { + Assert.IsNull(_server.ISupport.Watch); + + _server.ParseTokens(new Line("005 * WATCH=123 *")); + Assert.AreEqual(123, _server.ISupport.Watch); + + _server.ParseTokens(new Line("005 * WATCH *")); + Assert.AreEqual(-1, _server.ISupport.Watch); + } } } diff --git a/IrcStates/Tests/Motd.cs b/IrcStates/Tests/Motd.cs index 8ca7f07..403fa78 100644 --- a/IrcStates/Tests/Motd.cs +++ b/IrcStates/Tests/Motd.cs @@ -1,9 +1,23 @@ -using Microsoft.VisualStudio.TestTools.UnitTesting; +using System.Collections.Generic; +using IrcTokens; +using Microsoft.VisualStudio.TestTools.UnitTesting; namespace IrcStates.Tests { [TestClass] public class Motd { + [TestMethod] + public void MessageOfTheDay() + { + var server = new Server("test"); + server.ParseTokens(new Line("001 nickname")); + server.ParseTokens(new Line("375 * :start of motd")); + server.ParseTokens(new Line("372 * :first line of motd")); + server.ParseTokens(new Line("372 * :second line of motd")); + + CollectionAssert.AreEqual(new List {"start of motd", "first line of motd", "second line of motd"}, + server.Motd); + } } } diff --git a/IrcStates/Tests/Who.cs b/IrcStates/Tests/Who.cs index 359991c..feee60e 100644 --- a/IrcStates/Tests/Who.cs +++ b/IrcStates/Tests/Who.cs @@ -1,9 +1,61 @@ -using Microsoft.VisualStudio.TestTools.UnitTesting; +using IrcTokens; +using Microsoft.VisualStudio.TestTools.UnitTesting; namespace IrcStates.Tests { [TestClass] public class Who { + private Server _server; + + [TestInitialize] + public void TestInitialize() + { + _server = new Server("test"); + _server.ParseTokens(new Line("001 nickname")); + _server.ParseTokens(new Line(":nickname JOIN #chan")); + } + + [TestMethod] + public void WhoResponse() + { + _server.ParseTokens(new Line("352 * #chan user host server nickname * :0 real")); + var user = _server.Users["nickname"]; + + Assert.AreEqual("user", user.UserName); + Assert.AreEqual("host", _server.HostName); + Assert.AreEqual("real", user.RealName); + + Assert.AreEqual(user.UserName, _server.UserName); + Assert.AreEqual(user.HostName, _server.HostName); + Assert.AreEqual(user.RealName, _server.RealName); + } + + [TestMethod] + public void Whox() + { + _server.ParseTokens(new Line($"354 * {Server.WhoType} user realip host nickname account :real")); + var user = _server.Users["nickname"]; + + Assert.AreEqual("user", user.UserName); + Assert.AreEqual("host", user.HostName); + Assert.AreEqual("real", user.RealName); + Assert.AreEqual("account", user.Account); + + Assert.AreEqual(user.UserName, _server.UserName); + Assert.AreEqual(user.HostName, _server.HostName); + Assert.AreEqual(user.RealName, _server.RealName); + Assert.AreEqual(user.Account, _server.Account); + } + + [TestMethod] + public void WhoxNoAccount() + { + _server.ParseTokens(new Line($"354 * {Server.WhoType} user realip host nickname 0 :real")); + var user = _server.Users["nickname"]; + + Assert.IsNull(user.Account); + Assert.AreEqual(user.Account, _server.Account); + } } } diff --git a/README.md b/README.md index 1a559ec..1796da2 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,6 @@ # IrcSharp -[![Build Status](https://drone.tildegit.org/api/badges/ben/irctokens/status.svg)](https://drone.tildegit.org/ben/irctokens) +[![Build Status](https://drone.tildegit.org/api/badges/ben/ircsharp/status.svg)](https://drone.tildegit.org/ben/ircsharp) this is a collection of c\# projects to tokenize, parse, and maintain state for IRC clients.