IRC parsing, tokenization, and state handling in C#

add ISUPPORT tests

+411 -4
+81
IrcStates/ISupport.cs
··· 1 1 // ReSharper disable InconsistentNaming 2 2 3 + using System; 4 + using System.Collections.Generic; 5 + using System.Globalization; 6 + 3 7 namespace IrcStates 4 8 { 5 9 public class ISupport 6 10 { 11 + public ISupport() 12 + { 13 + Raw = new Dictionary<string, string>(); 14 + Modes = 3; 15 + CaseMapping = Casemap.CaseMapping.Rfc1459; 16 + // TODO: add remaining defaults, change properties to normal members as needed 17 + } 18 + 19 + public Dictionary<string, string> Raw { get; set; } 20 + public string Network { get; set; } 21 + public ISupportChanModes ChanModes { get; set; } 22 + public ISupportPrefix Prefix { get; set; } 23 + public int? Modes { get; set; } 24 + public Casemap.CaseMapping CaseMapping { get; set; } 25 + public List<string> ChanTypes { get; set; } 26 + public List<string> StatusMsg { get; set; } 27 + public string CallerId { get; set; } 28 + public string Excepts { get; set; } 29 + public string Invex { get; set; } 30 + public int? Monitor { get; set; } 31 + public int? Watch { get; set; } 32 + public bool Whox { get; set; } 33 + 34 + public void Parse(IList<string> tokens) 35 + { 36 + foreach (var token in tokens) 37 + { 38 + var split = token.Split('=', 2); 39 + var key = split[0]; 40 + var value = split[1]; 41 + 42 + if (split.Length > 1) Raw[key] = value; 43 + 44 + switch (split[0]) 45 + { 46 + case "NETWORK": 47 + Network = value; 48 + break; 49 + case "CHANMODES": 50 + ChanModes = new ISupportChanModes(value); 51 + break; 52 + case "PREFIX": 53 + Prefix = new ISupportPrefix(value); 54 + break; 55 + case "STATUSMSG": 56 + StatusMsg = new List<string> {value}; 57 + break; 58 + case "MODES": 59 + Modes = int.Parse(value, NumberStyles.Integer, CultureInfo.InvariantCulture); 60 + break; 61 + case "MONITOR": 62 + Monitor = int.Parse(value, NumberStyles.Integer, CultureInfo.InvariantCulture); 63 + break; 64 + case "WATCH": 65 + Watch = int.Parse(value, NumberStyles.Integer, CultureInfo.InvariantCulture); 66 + break; 67 + case "CASEMAPPING": 68 + if (Enum.TryParse(value, true, out Casemap.CaseMapping caseMapping)) CaseMapping = caseMapping; 69 + break; 70 + case "CHANTYPES": 71 + ChanTypes = new List<string> {value}; 72 + break; 73 + case "CALLERID": 74 + CallerId = string.IsNullOrEmpty(value) ? value : "g"; 75 + break; 76 + case "EXCEPTS": 77 + Excepts = string.IsNullOrEmpty(value) ? value : "e"; 78 + break; 79 + case "INVEX": 80 + Invex = string.IsNullOrEmpty(value) ? value : "I"; 81 + break; 82 + case "WHOX": 83 + Whox = true; 84 + break; 85 + } 86 + } 87 + } 7 88 } 8 89 }
+23
IrcStates/ISupportChanModes.cs
··· 1 + using System.Collections.Generic; 2 + 3 + namespace IrcStates 4 + { 5 + public class ISupportChanModes 6 + { 7 + public ISupportChanModes(string splitVal) 8 + { 9 + if (splitVal == null) return; 10 + 11 + var split = splitVal.Split(',', 4); 12 + ListModes = new List<string> {split[0]}; 13 + SettingBModes = new List<string> {split[1]}; 14 + SettingCModes = new List<string> {split[2]}; 15 + SettingDModes = new List<string> {split[3]}; 16 + } 17 + 18 + public List<string> ListModes { get; set; } 19 + public List<string> SettingBModes { get; set; } 20 + public List<string> SettingCModes { get; set; } 21 + public List<string> SettingDModes { get; set; } 22 + } 23 + }
+27
IrcStates/ISupportPrefix.cs
··· 1 + using System.Collections.Generic; 2 + 3 + namespace IrcStates 4 + { 5 + public class ISupportPrefix 6 + { 7 + public ISupportPrefix(string splitVal) 8 + { 9 + var split = splitVal.Substring(1).Split(')', 2); 10 + Modes = new List<string> {split[0]}; 11 + Prefixes = new List<string> {split[1]}; 12 + } 13 + 14 + public List<string> Modes { get; set; } 15 + public List<string> Prefixes { get; set; } 16 + 17 + public string FromMode(string mode) 18 + { 19 + return Modes.Contains(mode) ? Modes[Modes.IndexOf(mode)] : null; 20 + } 21 + 22 + public string FromPrefix(string prefix) 23 + { 24 + return Prefixes.Contains(prefix) ? Prefixes[Prefixes.IndexOf(prefix)] : null; 25 + } 26 + } 27 + }
+5
IrcStates/README.md
··· 1 + # IrcStates 2 + 3 + port of [jesopo/ircstates](https://github.com/jesopo/ircstates) 4 + 5 + bare bones irc client state
+6
IrcStates/Server.cs
··· 7 7 { 8 8 public class Server 9 9 { 10 + public const string WhoType = "525"; // randomly generated 10 11 private readonly StatefulDecoder _decoder; 11 12 12 13 public Dictionary<string, List<Func<string, Line, Emit>>> LineHandlers; ··· 80 81 } 81 82 82 83 return ret; 84 + } 85 + 86 + public string CaseFold(string str) 87 + { 88 + return Casemap.CaseFold(ISupport.CaseMapping, str); 83 89 } 84 90 } 85 91 }
+200 -1
IrcStates/Tests/ISupport.cs
··· 1 - using Microsoft.VisualStudio.TestTools.UnitTesting; 1 + using System.Collections.Generic; 2 + using IrcTokens; 3 + using Microsoft.VisualStudio.TestTools.UnitTesting; 2 4 3 5 // ReSharper disable InconsistentNaming 4 6 ··· 7 9 [TestClass] 8 10 public class ISupport 9 11 { 12 + private Server _server; 13 + 14 + [TestInitialize] 15 + public void TestInitialize() 16 + { 17 + _server = new Server("test"); 18 + _server.ParseTokens(new Line("001 nickname")); 19 + } 20 + 21 + [TestMethod] 22 + public void ChanModes() 23 + { 24 + CollectionAssert.AreEqual(new List<string> {"b"}, _server.ISupport.ChanModes.ListModes); 25 + CollectionAssert.AreEqual(new List<string> {"k"}, _server.ISupport.ChanModes.SettingBModes); 26 + CollectionAssert.AreEqual(new List<string> {"l"}, _server.ISupport.ChanModes.SettingCModes); 27 + CollectionAssert.AreEqual(new List<string> 28 + { 29 + "i", 30 + "m", 31 + "n", 32 + "p", 33 + "s", 34 + "t" 35 + }, _server.ISupport.ChanModes.SettingDModes); 36 + 37 + _server.ParseTokens(new Line("005 * CHANMODES=a,b,c,d *")); 38 + 39 + CollectionAssert.AreEqual(new List<string> {"a"}, _server.ISupport.ChanModes.ListModes); 40 + CollectionAssert.AreEqual(new List<string> {"b"}, _server.ISupport.ChanModes.SettingBModes); 41 + CollectionAssert.AreEqual(new List<string> {"c"}, _server.ISupport.ChanModes.SettingCModes); 42 + CollectionAssert.AreEqual(new List<string> {"d"}, _server.ISupport.ChanModes.SettingDModes); 43 + } 44 + 45 + [TestMethod] 46 + public void Prefix() 47 + { 48 + CollectionAssert.AreEqual(new List<string> {"o", "v"}, _server.ISupport.Prefix.Modes); 49 + CollectionAssert.AreEqual(new List<string> {"@", "+"}, _server.ISupport.Prefix.Prefixes); 50 + 51 + Assert.AreEqual("@", _server.ISupport.Prefix.FromMode("o")); 52 + Assert.IsNull(_server.ISupport.Prefix.FromMode("a")); 53 + Assert.AreEqual("o", _server.ISupport.Prefix.FromPrefix("@")); 54 + Assert.IsNull(_server.ISupport.Prefix.FromPrefix("&")); 55 + 56 + _server.ParseTokens(new Line("005 * PREFIX=(qaohv)~&#%+ *")); 57 + CollectionAssert.AreEqual(new List<string> 58 + { 59 + "q", 60 + "a", 61 + "o", 62 + "h", 63 + "v" 64 + }, _server.ISupport.Prefix.Modes); 65 + CollectionAssert.AreEqual(new List<string> 66 + { 67 + "~", 68 + "&", 69 + "@", 70 + "%", 71 + "+" 72 + }, _server.ISupport.Prefix.Prefixes); 73 + Assert.AreEqual("&", _server.ISupport.Prefix.FromMode("a")); 74 + Assert.AreEqual("a", _server.ISupport.Prefix.FromPrefix("&")); 75 + } 76 + 77 + [TestMethod] 78 + public void ChanTypes() 79 + { 80 + CollectionAssert.AreEqual(new List<string> {"#"}, _server.ISupport.ChanTypes); 81 + _server.ParseTokens(new Line("005 * CHANTYPES=#& *")); 82 + CollectionAssert.AreEqual(new List<string> {"#", "&"}, _server.ISupport.ChanTypes); 83 + } 84 + 85 + [TestMethod] 86 + public void Modes() 87 + { 88 + Assert.AreEqual(3, _server.ISupport.Modes); 89 + 90 + _server.ParseTokens(new Line("005 * MODES *")); 91 + Assert.AreEqual(-1, _server.ISupport.Modes); 92 + 93 + _server.ParseTokens(new Line("005 * MODES=5 *")); 94 + Assert.AreEqual(5, _server.ISupport.Modes); 95 + } 96 + 97 + [TestMethod] 98 + public void Rfc1459() 99 + { 100 + Assert.AreEqual(IrcStates.Casemap.CaseMapping.Rfc1459, _server.ISupport.CaseMapping); 101 + _server.ParseTokens(new Line("005 * CASEMAPPING=rfc1459 *")); 102 + Assert.AreEqual(IrcStates.Casemap.CaseMapping.Rfc1459, _server.ISupport.CaseMapping); 103 + var lower = _server.CaseFold(@"ÀTEST[]~\"); 104 + Assert.AreEqual("Àtest{}^|", lower); 105 + } 106 + 107 + [TestMethod] 108 + public void Ascii() 109 + { 110 + _server.ParseTokens(new Line("005 * CASEMAPPING=ascii *")); 111 + Assert.AreEqual(IrcStates.Casemap.CaseMapping.Ascii, _server.ISupport.CaseMapping); 112 + var lower = _server.CaseFold(@"ÀTEST[]~\"); 113 + Assert.AreEqual(@"Àtest[]~\", lower); 114 + } 115 + 116 + [TestMethod] 117 + public void FallbackToRfc1459() 118 + { 119 + _server.ParseTokens(new Line("005 * CASEMAPPING=nonexistent *")); 120 + Assert.AreEqual(IrcStates.Casemap.CaseMapping.Rfc1459, _server.ISupport.CaseMapping); 121 + var lower = _server.CaseFold(@"ÀTEST[]~\"); 122 + Assert.AreEqual("Àtest{}^|", lower); 123 + } 124 + 125 + [TestMethod] 126 + public void Network() 127 + { 128 + Assert.IsNull(_server.ISupport.Network); 129 + _server.ParseTokens(new Line("005 * NETWORK=testnet *")); 130 + Assert.AreEqual("testnet", _server.ISupport.Network); 131 + } 132 + 133 + [TestMethod] 134 + public void StatusMsg() 135 + { 136 + CollectionAssert.AreEqual(new List<string>(), _server.ISupport.StatusMsg); 137 + _server.ParseTokens(new Line("005 * STATUSMSG=&@ *")); 138 + CollectionAssert.AreEqual(new List<string> {"&", "@"}, _server.ISupport.StatusMsg); 139 + } 140 + 141 + [TestMethod] 142 + public void CallerId() 143 + { 144 + Assert.IsNull(_server.ISupport.CallerId); 145 + 146 + _server.ParseTokens(new Line("005 * CALLERID=U *")); 147 + Assert.AreEqual("U", _server.ISupport.CallerId); 148 + 149 + _server.ParseTokens(new Line("005 * CALLERID *")); 150 + Assert.AreEqual("g", _server.ISupport.CallerId); 151 + } 152 + 153 + [TestMethod] 154 + public void Excepts() 155 + { 156 + Assert.IsNull(_server.ISupport.Excepts); 157 + 158 + _server.ParseTokens(new Line("005 * EXCEPTS=U *")); 159 + Assert.AreEqual("U", _server.ISupport.Excepts); 160 + 161 + _server.ParseTokens(new Line("005 * EXCEPTS *")); 162 + Assert.AreEqual("e", _server.ISupport.Excepts); 163 + } 164 + 165 + [TestMethod] 166 + public void Invex() 167 + { 168 + Assert.IsNull(_server.ISupport.Invex); 169 + 170 + _server.ParseTokens(new Line("005 * INVEX=U *")); 171 + Assert.AreEqual("U", _server.ISupport.Invex); 172 + 173 + _server.ParseTokens(new Line("005 * INVEX *")); 174 + Assert.AreEqual("I", _server.ISupport.Invex); 175 + } 176 + 177 + [TestMethod] 178 + public void Whox() 179 + { 180 + Assert.IsFalse(_server.ISupport.Whox); 181 + 182 + _server.ParseTokens(new Line("005 * WHOX *")); 183 + Assert.IsTrue(_server.ISupport.Whox); 184 + } 185 + 186 + [TestMethod] 187 + public void Monitor() 188 + { 189 + Assert.IsNull(_server.ISupport.Monitor); 190 + 191 + _server.ParseTokens(new Line("005 * MONITOR=123 *")); 192 + Assert.AreEqual(123, _server.ISupport.Monitor); 193 + 194 + _server.ParseTokens(new Line("005 * MONITOR *")); 195 + Assert.AreEqual(-1, _server.ISupport.Monitor); 196 + } 197 + 198 + [TestMethod] 199 + public void Watch() 200 + { 201 + Assert.IsNull(_server.ISupport.Watch); 202 + 203 + _server.ParseTokens(new Line("005 * WATCH=123 *")); 204 + Assert.AreEqual(123, _server.ISupport.Watch); 205 + 206 + _server.ParseTokens(new Line("005 * WATCH *")); 207 + Assert.AreEqual(-1, _server.ISupport.Watch); 208 + } 10 209 } 11 210 }
+15 -1
IrcStates/Tests/Motd.cs
··· 1 - using Microsoft.VisualStudio.TestTools.UnitTesting; 1 + using System.Collections.Generic; 2 + using IrcTokens; 3 + using Microsoft.VisualStudio.TestTools.UnitTesting; 2 4 3 5 namespace IrcStates.Tests 4 6 { 5 7 [TestClass] 6 8 public class Motd 7 9 { 10 + [TestMethod] 11 + public void MessageOfTheDay() 12 + { 13 + var server = new Server("test"); 14 + server.ParseTokens(new Line("001 nickname")); 15 + server.ParseTokens(new Line("375 * :start of motd")); 16 + server.ParseTokens(new Line("372 * :first line of motd")); 17 + server.ParseTokens(new Line("372 * :second line of motd")); 18 + 19 + CollectionAssert.AreEqual(new List<string> {"start of motd", "first line of motd", "second line of motd"}, 20 + server.Motd); 21 + } 8 22 } 9 23 }
+53 -1
IrcStates/Tests/Who.cs
··· 1 - using Microsoft.VisualStudio.TestTools.UnitTesting; 1 + using IrcTokens; 2 + using Microsoft.VisualStudio.TestTools.UnitTesting; 2 3 3 4 namespace IrcStates.Tests 4 5 { 5 6 [TestClass] 6 7 public class Who 7 8 { 9 + private Server _server; 10 + 11 + [TestInitialize] 12 + public void TestInitialize() 13 + { 14 + _server = new Server("test"); 15 + _server.ParseTokens(new Line("001 nickname")); 16 + _server.ParseTokens(new Line(":nickname JOIN #chan")); 17 + } 18 + 19 + [TestMethod] 20 + public void WhoResponse() 21 + { 22 + _server.ParseTokens(new Line("352 * #chan user host server nickname * :0 real")); 23 + var user = _server.Users["nickname"]; 24 + 25 + Assert.AreEqual("user", user.UserName); 26 + Assert.AreEqual("host", _server.HostName); 27 + Assert.AreEqual("real", user.RealName); 28 + 29 + Assert.AreEqual(user.UserName, _server.UserName); 30 + Assert.AreEqual(user.HostName, _server.HostName); 31 + Assert.AreEqual(user.RealName, _server.RealName); 32 + } 33 + 34 + [TestMethod] 35 + public void Whox() 36 + { 37 + _server.ParseTokens(new Line($"354 * {Server.WhoType} user realip host nickname account :real")); 38 + var user = _server.Users["nickname"]; 39 + 40 + Assert.AreEqual("user", user.UserName); 41 + Assert.AreEqual("host", user.HostName); 42 + Assert.AreEqual("real", user.RealName); 43 + Assert.AreEqual("account", user.Account); 44 + 45 + Assert.AreEqual(user.UserName, _server.UserName); 46 + Assert.AreEqual(user.HostName, _server.HostName); 47 + Assert.AreEqual(user.RealName, _server.RealName); 48 + Assert.AreEqual(user.Account, _server.Account); 49 + } 50 + 51 + [TestMethod] 52 + public void WhoxNoAccount() 53 + { 54 + _server.ParseTokens(new Line($"354 * {Server.WhoType} user realip host nickname 0 :real")); 55 + var user = _server.Users["nickname"]; 56 + 57 + Assert.IsNull(user.Account); 58 + Assert.AreEqual(user.Account, _server.Account); 59 + } 8 60 } 9 61 }
+1 -1
README.md
··· 1 1 # IrcSharp 2 2 3 - [![Build Status](https://drone.tildegit.org/api/badges/ben/irctokens/status.svg)](https://drone.tildegit.org/ben/irctokens) 3 + [![Build Status](https://drone.tildegit.org/api/badges/ben/ircsharp/status.svg)](https://drone.tildegit.org/ben/ircsharp) 4 4 5 5 this is a collection of c\# projects to tokenize, parse, and maintain 6 6 state for IRC clients.