IRC parsing, tokenization, and state handling in C#

Add sample project

+132 -3
+7 -1
IrcTokens.sln
··· 3 3 # Visual Studio Version 16 4 4 VisualStudioVersion = 16.0.30011.22 5 5 MinimumVisualStudioVersion = 10.0.40219.1 6 - Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "IrcTokens", "IrcTokens\IrcTokens.csproj", "{9E812F45-B2CD-42D2-8378-EBEBF8697905}" 6 + Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "IrcTokens", "IrcTokens\IrcTokens.csproj", "{9E812F45-B2CD-42D2-8378-EBEBF8697905}" 7 + EndProject 8 + Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Sample", "Sample\Sample.csproj", "{A45DA39B-6B47-4713-8049-3B36E0235B67}" 7 9 EndProject 8 10 Global 9 11 GlobalSection(SolutionConfigurationPlatforms) = preSolution ··· 15 17 {9E812F45-B2CD-42D2-8378-EBEBF8697905}.Debug|Any CPU.Build.0 = Debug|Any CPU 16 18 {9E812F45-B2CD-42D2-8378-EBEBF8697905}.Release|Any CPU.ActiveCfg = Release|Any CPU 17 19 {9E812F45-B2CD-42D2-8378-EBEBF8697905}.Release|Any CPU.Build.0 = Release|Any CPU 20 + {A45DA39B-6B47-4713-8049-3B36E0235B67}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 21 + {A45DA39B-6B47-4713-8049-3B36E0235B67}.Debug|Any CPU.Build.0 = Debug|Any CPU 22 + {A45DA39B-6B47-4713-8049-3B36E0235B67}.Release|Any CPU.ActiveCfg = Release|Any CPU 23 + {A45DA39B-6B47-4713-8049-3B36E0235B67}.Release|Any CPU.Build.0 = Release|Any CPU 18 24 EndGlobalSection 19 25 GlobalSection(SolutionProperties) = preSolution 20 26 HideSolutionNode = FALSE
+15 -2
IrcTokens/Line.cs
··· 18 18 private Hostmask _hostmask; 19 19 private readonly string _rawLine; 20 20 21 - public override string ToString() => 22 - $"Line(source={Source}, command={Command}, tags={string.Join(";", Tags.Select(kvp => $"{kvp.Key}={kvp.Value}"))}, params={string.Join(",", Params)})"; 21 + public override string ToString() 22 + { 23 + var vars = new List<string>(); 24 + 25 + if (Command != null) 26 + vars.Add($"command={Command}"); 27 + if (Source != null) 28 + vars.Add($"source={Source}"); 29 + if (Params != null && Params.Any()) 30 + vars.Add($"params=[{string.Join(",", Params)}]"); 31 + if (Tags != null && Tags.Any()) 32 + vars.Add($"tags=[{string.Join(";", Tags.Select(kvp => $"{kvp.Key}={kvp.Value}"))}]"); 33 + 34 + return $"Line({string.Join(", ", vars)})"; 35 + } 23 36 24 37 public override int GetHashCode() => Format().GetHashCode(StringComparison.Ordinal); 25 38
+68
Sample/Client.cs
··· 1 + using System; 2 + using System.Collections.Generic; 3 + using System.Net.Sockets; 4 + using System.Text; 5 + using IrcTokens; 6 + 7 + namespace Sample 8 + { 9 + public class Client 10 + { 11 + private readonly Socket _socket; 12 + private readonly StatefulDecoder _decoder; 13 + private readonly StatefulEncoder _encoder; 14 + private readonly byte[] _bytes; 15 + 16 + public Client() 17 + { 18 + _decoder = new StatefulDecoder(); 19 + _encoder = new StatefulEncoder(); 20 + _socket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.IP); 21 + _bytes = new byte[1024]; 22 + } 23 + 24 + public void Start() 25 + { 26 + _socket.Connect("127.0.0.1", 6667); 27 + 28 + Send(new Line {Command = "USER", Params = new List<string> {"username", "0", "*", "real name"}}); 29 + Send(new Line {Command = "NICK", Params = new List<string> {"statefulbot"}}); 30 + 31 + while (true) 32 + { 33 + var bytesReceived = _socket.Receive(_bytes); 34 + var lines = _decoder.Push(_bytes); 35 + 36 + if (lines.Count == 0) 37 + { 38 + Console.WriteLine("! disconnected"); 39 + _socket.Shutdown(SocketShutdown.Both); 40 + break; 41 + } 42 + 43 + foreach (var line in lines) 44 + { 45 + Console.WriteLine($"< {line.Format()}"); 46 + 47 + switch (line.Command) 48 + { 49 + case "PING": 50 + Send(new Line {Command = "PONG", Params = line.Params}); 51 + break; 52 + case "001": 53 + Send(new Line {Command = "JOIN", Params = new List<string> {"#channel"}}); 54 + break; 55 + } 56 + } 57 + } 58 + } 59 + 60 + private void Send(Line line) 61 + { 62 + Console.WriteLine($"> {line.Format()}"); 63 + _encoder.Push(line); 64 + while (_encoder.PendingBytes.Length > 0) 65 + _encoder.Pop(_socket.Send(_encoder.PendingBytes)); 66 + } 67 + } 68 + }
+30
Sample/Program.cs
··· 1 + using System; 2 + using System.Collections.Generic; 3 + using IrcTokens; 4 + 5 + namespace Sample 6 + { 7 + public class Program 8 + { 9 + public static void Main(string[] args) 10 + { 11 + // tokenization 12 + var line = new Line("@id=123 :ben!~ben@hostname PRIVMSG #channel :hello there!"); 13 + Console.WriteLine(line); 14 + Console.WriteLine(line.Format()); 15 + 16 + // formatting 17 + var line2 = new Line 18 + { 19 + Command = "USER", 20 + Params = new List<string> {"user", "0", "*", "real name"} 21 + }; 22 + Console.WriteLine(line2); 23 + Console.WriteLine(line2.Format()); 24 + 25 + // stateful example 26 + var client = new Client(); 27 + client.Start(); 28 + } 29 + } 30 + }
+12
Sample/Sample.csproj
··· 1 + <Project Sdk="Microsoft.NET.Sdk"> 2 + 3 + <PropertyGroup> 4 + <OutputType>Exe</OutputType> 5 + <TargetFramework>netcoreapp3.1</TargetFramework> 6 + </PropertyGroup> 7 + 8 + <ItemGroup> 9 + <ProjectReference Include="..\IrcTokens\IrcTokens.csproj" /> 10 + </ItemGroup> 11 + 12 + </Project>