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