IRC parsing, tokenization, and state handling in C#
at main 85 lines 2.5 kB view raw
1using System.Net.Sockets; 2using IRCStates; 3using IRCTokens; 4 5// ReSharper disable StringLiteralTypo 6 7namespace States; 8 9internal class Client(string host, int port, string nick) 10{ 11 private readonly byte[] _bytes = new byte[1024]; 12 private readonly StatefulEncoder _encoder = new(); 13 private readonly Server _server = new("test"); 14 private readonly Socket _socket = new(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp); 15 16 private void Send(string raw) 17 { 18 _encoder.Push(new Line(raw)); 19 } 20 21 public void Start() 22 { 23 _socket.Connect(host, port); 24 while (!_socket.Connected) 25 { 26 Thread.Sleep(1000); 27 } 28 29 Send("USER test 0 * test"); 30 Send($"NICK {nick}"); 31 32 while (true) 33 { 34 while (_encoder.PendingBytes.Length != 0) 35 { 36 foreach (var line in _encoder.Pop(_socket.Send(_encoder.PendingBytes))) 37 { 38 Console.WriteLine($"> {line.Format()}"); 39 } 40 } 41 42 var bytesReceived = _socket.Receive(_bytes); 43 if (bytesReceived == 0) 44 { 45 Console.WriteLine("! disconnected"); 46 _socket.Shutdown(SocketShutdown.Both); 47 _socket.Close(); 48 break; 49 } 50 51 var receivedLines = _server.Receive(_bytes, bytesReceived); 52 foreach (var (line, _) in receivedLines) 53 { 54 Console.WriteLine($"< {line.Format()}"); 55 56 switch (line.Command) 57 { 58 case Commands.Privmsg: 59 if (line.Params[1].Contains(_server.NickName)) 60 { 61 Send($"PRIVMSG {line.Params[0]} :hi {line.Hostmask.NickName}!"); 62 } 63 64 break; 65 case "PING": Send($"PONG :{line.Params[0]}"); break; 66 case Numeric.RPL_WELCOME: 67 if (!_server.HasChannel("#irctokens")) 68 { 69 Send("JOIN #irctokens"); 70 } 71 72 break; 73 case "INVITE": 74 var c = line.Params[1]; 75 if (!_server.HasChannel(c)) 76 { 77 Send($"JOIN {c}"); 78 } 79 80 break; 81 } 82 } 83 } 84 } 85}