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